Engee documentation
Notebook

The Rock-Paper-Scissors game

This example shows the code of a web application for the Rock-Paper-Scissors game with a graphical interface implemented using HTML and JavaScript. Let's take it apart piece by piece.

HTML structure

The code starts with standard HTML markup.:

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Rock-paper-scissors</title>

Styles (CSS)

In the section <style> several CSS rules are defined:

#reset-btn:hover {
	transform: scale(1.2);
	transition: transform 0.2s;
}
.choice-btn:hover:not(:disabled) {
	transform: scale(1.1);
	transition: transform 0.2s;
}
.choice-btn:disabled {
	opacity: 0.5;
	cursor: not-allowed;
}

These styles add:

  • animation of the zoom of the reset button when hovering;
  • animation of magnification of the selection buttons when hovering (if they are not disabled);
  • style for disabled selection buttons (semi-transparent with the cursor "not allowed").

The basic structure of the page

The body of the document contains a container with the game:

<div style="background: #f0f0f0; font-family: Arial, sans-serif; max-width: 400px; margin: 0 auto; padding: 20px; border-radius: 20px; position: relative;">

There are several sections inside it.

  1. Score Counter:
<div style="display: flex; justify-content: center; align-items: center; gap: 20px; margin-bottom: 10px;">
	<div style="background: white; padding: 15px; border-radius: 10px; text-align: center; font-size: 18px; flex-grow: 1;">
		<div style="display: flex; justify-content: space-around; align-items: center;">
			<div style="display: flex; align-items: center; gap: 5px;">
				<div style="font-weight: bold;">YOU:</div>
				<div id="player-score" style="font-size: 24px;">0️⃣</div>
			</div>
			<div style="font-weight: bold; font-size: 20px;">VS</div>
			<div style="display: flex; align-items: center; gap: 5px;">
				<div style="font-weight: bold;">Robot:</div>
				<div id="computer-score" style="font-size: 24px;">0️⃣</div>
			</div>
		</div>
		<div id="result-text"></div>
	</div>
	<button onclick="resetGame()" id="reset-btn" style="background: none; border: none; cursor: pointer; font-size: 24px; margin-left: 10px;">🔄</button>
</div>
  1. Player and computer selection area:
<div style="display: flex; justify-content: space-between; margin-bottom: 20px; height: 100px; align-items: center;">
    <div style="text-align: center; width: 50%; position: relative;">
        <div style="position: relative; display: inline-block; vertical-align: middle; line-height: 0;">
            <div id="player-choice" style="font-size: 80px;">🤠</div>
            <div id="player-mini-choice" style="..."></div>
        </div>
    </div>
    <div style="text-align: center; width: 50%; position: relative;">
        <div style="position: relative; display: inline-block; vertical-align: middle; line-height: 0;">
            <div id="computer-choice" style="font-size: 80px;">🤖</div>
            <div id="computer-mini-choice" style="..."></div>
        </div>
    </div>
</div>
  1. Selection buttons:
<div style="text-align: center; margin-bottom: 15px;">
	<h3 style="margin-bottom: 10px; color: #444;">Your choice:</h3>
	<div style="display: flex; justify-content: center; gap: 10px; margin-bottom: 15px;">
		<button onclick="makeChoice(0)" class="choice-btn" style="..."></button>
		<button onclick="makeChoice(1)" class="choice-btn" style="..."></button>
		<button onclick="makeChoice(2)" class="choice-btn" style="...">✌️</button>
	</div>
</div>

JavaScript logic

Initializing variables

let playerScore = 0;
let computerScore = 0;
let gameActive = true;

const choices = ['✊', '✋', '✌️'];
const beats = {
	'✊': '✌️',
	'✋': '✊',
	'✌️': '✋'
};

Auxiliary functions

  1. updateScoreEmoji updates the account using emojis:
function updateScoreEmoji(scoreElement, score) {
	const emojiScores = ['0️⃣', '1️⃣', '2️⃣', '3️⃣'];
	scoreElement.textContent = emojiScores[score] || score;
}
  1. animateComputerChoice animates computer selection:
function animateComputerChoice(finalChoice, callback) {
	let iterations = 0;
	const animationInterval = setInterval(() => {
		document.getElementById('computer-choice').textContent = choices[iterations % 3];
		iterations++;
		if (iterations > 10) {
			clearInterval(animationInterval);
			document.getElementById('computer-choice').textContent = '🤖';
			document.getElementById('computer-mini-choice').textContent = finalChoice;
			if (callback) callback();
		}
	}, 100);
}

The main functions of the game

  1. makeChoice – player's choice handler:
function makeChoice(choiceIdx) {
	if (!gameActive) {
alert('The game is over. Click 🔄 for a new one.');
		return;
	}
	const playerEmoji = choices[choiceIdx];
	const computerEmoji = choices[Math.floor(Math.random() * 3)];

	// Updating the interface
	document.getElementById('player-mini-choice').textContent = playerEmoji;
	document.getElementById('player-choice').textContent = '🤠';
	document.querySelectorAll('.choice-btn').forEach(btn => btn.disabled = true);
	// Determining the winner
	function getWinner(player, computer) {
		if (player === computer) return 'draw';
		return beats[player] === computer ? 'player' : 'computer';
	}
	const winner = getWinner(playerEmoji, computerEmoji);

	// Updating the score
if (winner === 'player') {
PlayerScore++;
	} else if (winner === 'computer') {
computerScore++;
}
// Checking the end of the game
if (PlayerScore === 3 || computerScore === 3) {
gameActive = false;
	}

	// Animating the computer selection and updating the interface
	animateComputerChoice(computerEmoji, () => {
		updateScoreEmoji(document.getElementById('player-score'), playerScore);
		updateScoreEmoji(document.getElementById('computer-score'), computerScore);

		// Showing the result
		let resultText = '';
		if (!gameActive) {
			if (playerScore === 3) {
				resultText = '🏆 The player has won!';
			} else {
				resultText = '🤖 The robot has won!';
			}
		} else {
			if (winner === 'draw') {
resultText = 'Draw!';
			} else if (winner === 'player') {
				resultText = 'You have won this round!';
			} else {
				resultText = 'The robot has won this round!';
			}
		}
		document.getElementById('result-text').textContent = resultText;

		// Turn on/off the buttons depending on the state of the game
		document.querySelectorAll('.choice-btn').forEach(btn => {
			btn.disabled = !gameActive;
		});
	});
}
  1. resetGame – reset the game:
function resetGame() {
	playerScore = 0;
	computerScore = 0;
	gameActive = true;
	updateScoreEmoji(document.getElementById('player-score'), playerScore);
	updateScoreEmoji(document.getElementById('computer-score'), computerScore);
	document.getElementById('player-choice').textContent = '🤠';
	document.getElementById('computer-choice').textContent = '🤖';
	document.getElementById('player-mini-choice').textContent = '';
	document.getElementById('computer-mini-choice').textContent = '';
	document.getElementById('result-text').textContent = '';
	document.querySelectorAll('.choice-btn').forEach(btn => btn.disabled = false);
}

This code is a complete application with an interface and all the necessary functions for the Rock-paper-Scissors game.

Now let's move on to connecting our app to Engee. The easiest option is to call and display an HTML file directly in .ngscript.

In [ ]:
@time display(MIME("text/html"), read("$(@__DIR__)/GUI.html", String))
Камень-Ножницы-Бумага
ВЫ:
0️⃣
VS
Робот:
0️⃣
🤠
🤖

Ваш выбор:

  0.396500 seconds (51.99 k allocations: 3.394 MiB, 99.28% compilation time)

Alternatively, we can raise the server using GenieFramework by calling the App.jl file.

using GenieFramework

route("/") do
    html(read(joinpath(@__DIR__, "GUI.html"), String))
end
In [ ]:
Pkg.add("GenieFramework")
using GenieFramework
In [ ]:
# Запускаем приложение и получаем URL
app_url = string(engee.genie.start(string(@__DIR__,"/App.jl")))
# Извлекаем URL с помощью регулярного выражения
url_match = match(r"'(https?://[^']+)'", app_url)
url = url_match[1]  # Получаем URL из первой группы захвата
Out[0]:
"https://engee.com/prod/user/demo54365636-yurev/genie/App/"

Based on the results of the server launch, we can connect to the server via URL and display the application in iframe without leaving Engee.

An iframe (Inline Frame) is an HTML element that allows you to embed another HTML document or web page inside the current page. This creates a "window" inside the page where independent content can be displayed.

html_content = """<iframe src="$url" width="750" height="500" style="border: none;"></iframe>"""

display(MIME("text/html"), html_content)

Conclusion

The code of the Rock-Paper-Scissors game is a complete web application. Analyzing the calling methods and their features, the following observations can be made.

  1. Direct HTML output:
  • simple demonstration without a backend;
    • works in a local context Engee;
    • Limited integration with other components.
  1. Via GenieFramework:
    • runs a full-fledged web server,
    • allows you to embed a game in an iframe,
    • suitable for integration into larger projects,
    • requires more time to start up,
    • provides isolation of the execution context.