Engee documentation
Notebook

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 break it down piece by piece.

HTML structure

The code starts with standard HTML markup:

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Stone-Scissors-Paper</title>

Styles (CSS)

There are several CSS rules defined in the <style> section:

#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 reset button magnification when hovering;
  • zoom animation for selection buttons when hovering (if they are not disabled);
  • style for disabled selection buttons (semi-transparent with a "not allowed" cursor).

Basic page structure

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

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

There are several sections within it.

  1. Score Counter:
html.
<div style="display: flex; justify-content: centre; align-items: centre; gap: 20px; margin-bottom: 10px;">
	<div style="background: white; padding: 15px; border-radius: 10px; text-align: centre; font-size: 18px; flex-grow: 1;">
		<div style="display: flex; justify-content: space-around; align-items: centre;">
			<div style="display: flex; align-items: centre; 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: centre; 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: centre;">
    <div style="text-align: centre; 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: centre; 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. Select Buttons:
<div style="text-align: centre; margin-bottom: 15px;">
	<h3 style="margin-bottom: 10px; colour: #444;">Your choice:</h3>
	<div style="display: flex; justify-content: centre; margin: 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

Variable initialisation

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

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

Auxiliary functions

  1. updateScoreEmoji updates the account with an emoji:
function updateScoreEmoji(scoreElement, score) {
	const emojiScores = ['0️⃣', '1️⃣', '2️⃣', '3️⃣'];
	scoreElement.textContent = emojiScores[score] || score;
}
  1. animateComputerChoice animates the 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 are

  1. makeChoice - Player selection handler:
function makeChoice(choiceIdx) {
	if (!gameActive) {
		alert('Game is over. press 🔄 for a new one.');
		return;
	}
	const playerEmoji = choices[choiceIdx];
	const computerEmoji = choices[Math.floor(Math.random() * 3)];

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

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

	// Animate Computer Choice and update the interface
	animateComputerChoice(computerEmoji, () => {
		updateScoreEmoji(document.getElementById('player-score'), playerScore);
		updateScoreEmoji(document.getElementById('computer-score'), computerScore);

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

		// Switch buttons on/off depending on the game state
		document.querySelectorAll('.choice-btn').forEach(btn => {
			btn.disabled = !gameActive;
		});
	});
}
  1. resetGame - game reset:
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 features for the Rock-Paper-Scissors game.

Now let's move on to connecting our application 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)

Or we can bring up 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 running the server, we can connect to the server at URL and display the application in an 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" within the page where independent content can be displayed.

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

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

Conclusion

**The code for the game "Rock-Paper-Scissors-Paper" ** is a complete web application. By analysing the invocation methods and their features, the following observations can be made.

  1. Direct HTML output:

    • simple serverless demo;
    • works in local context *Engee;
    • limited integration with other components.
  2. Through GenieFramework:

    • runs a full web server,
    • allows embedding the game in an iframe,
    • suitable for integration into larger projects,
    • requires more time to run,
    • provides runtime context isolation.