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>Камень-ножницы-бумага</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;">ВЫ:</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;">Робот:</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;">Ваш выбор:</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('Игра окончена. Нажмите 🔄 для новой.');
		return;
	}
	const playerEmoji = choices[choiceIdx];
	const computerEmoji = choices[Math.floor(Math.random() * 3)];

	// Обновляем интерфейс
	document.getElementById('player-mini-choice').textContent = playerEmoji;
	document.getElementById('player-choice').textContent = '🤠';
	document.querySelectorAll('.choice-btn').forEach(btn => btn.disabled = true);
	// Определяем победителя
	function getWinner(player, computer) {
		if (player === computer) return 'draw';
		return beats[player] === computer ? 'player' : 'computer';
	}
	const winner = getWinner(playerEmoji, computerEmoji);

	// Обновляем счет
	if (winner === 'player') {
		playerScore++;
	} else if (winner === 'computer') {
		computerScore++;
	}
	// Проверяем конец игры
	if (playerScore === 3 || computerScore === 3) {
		gameActive = false;
	}

	// Анимируем выбор компьютера и обновляем интерфейс
	animateComputerChoice(computerEmoji, () => {
		updateScoreEmoji(document.getElementById('player-score'), playerScore);
		updateScoreEmoji(document.getElementById('computer-score'), computerScore);

		// Показываем результат
		let resultText = '';
		if (!gameActive) {
			if (playerScore === 3) {
				resultText = '🏆 Победил игрок!';
			} else {
				resultText = '🤖 Победил робот!';
			}
		} else {
			if (winner === 'draw') {
				resultText = 'Ничья!';
			} else if (winner === 'player') {
				resultText = 'Вы выиграли этот раунд!';
			} else {
				resultText = 'Робот выиграл в этом раунде!';
			}
		}
		document.getElementById('result-text').textContent = resultText;

		// Включаем/выключаем кнопки в зависимости от состояния игры
		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 the 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 [ ]:
# Launch the application and get the URL
app_url = string(engee.genie.start(string(@__DIR__,"/App.jl")))
# Extracting the URL using a regular expression
url_match = match(r"'(https?://[^']+)'", app_url)
url = url_match[1]  # Getting the URL from the first capture group
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.

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.