first version
This commit is contained in:
parent
6122267fd1
commit
5d61ec735c
173
game_manager.js
Normal file
173
game_manager.js
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
class GameManager
|
||||||
|
{
|
||||||
|
constructor(grid, sidebar_div)
|
||||||
|
{
|
||||||
|
this.grid = grid;
|
||||||
|
this.sidebar_div = sidebar_div;
|
||||||
|
this.level = 0;
|
||||||
|
this.blocked = false;
|
||||||
|
this.default_opacity = default_opacity;
|
||||||
|
this.n_selected = 0;
|
||||||
|
|
||||||
|
this.grid.register_click_callback((i,j) => this.click_listener(i,j));
|
||||||
|
}
|
||||||
|
|
||||||
|
register_level_callback(func)
|
||||||
|
{
|
||||||
|
this.level_callback = func;
|
||||||
|
func(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
register_statustext_callback(func)
|
||||||
|
{
|
||||||
|
this.status_callback = func;
|
||||||
|
func("click 'start' to begin a new game");
|
||||||
|
}
|
||||||
|
|
||||||
|
click_listener(x,y)
|
||||||
|
{
|
||||||
|
var i = this.coords2index(y,x); //TODO: change index order
|
||||||
|
|
||||||
|
if (this.level <= 0)
|
||||||
|
{
|
||||||
|
//we're not ingame
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.active_cells.includes(i))
|
||||||
|
{
|
||||||
|
this.n_selected++;
|
||||||
|
if (this.n_selected == this.level)
|
||||||
|
{
|
||||||
|
this.on_level_up();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.on_game_over();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
restart()
|
||||||
|
{
|
||||||
|
if (!this.blocked)
|
||||||
|
{
|
||||||
|
this.sidebar_div.style.backgroundColor = "rgba(0,255,0," + this.default_opacity + ")";
|
||||||
|
this.level = 0;
|
||||||
|
this.level_callback(0);
|
||||||
|
this.grid.deactivate_all();
|
||||||
|
this.inactive_cells = [];
|
||||||
|
var i = 0;
|
||||||
|
for(i = 0; i < this.grid.width * this.grid.height; i++)
|
||||||
|
{
|
||||||
|
this.inactive_cells.push(i);
|
||||||
|
}
|
||||||
|
this.active_cells = [];
|
||||||
|
this.on_level_up();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
create_new_sequence()
|
||||||
|
{
|
||||||
|
// get one cell randomly
|
||||||
|
var i = Math.floor(Math.random()*this.inactive_cells.length);
|
||||||
|
var item = this.inactive_cells[i];
|
||||||
|
this.inactive_cells.splice(i, 1);
|
||||||
|
|
||||||
|
this.active_cells.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
show_sequence()
|
||||||
|
{
|
||||||
|
this.status_callback("memorize new sequence");
|
||||||
|
this.grid.deactivate_all();
|
||||||
|
var i;
|
||||||
|
for (i=0; i < this.active_cells.length; i++)
|
||||||
|
{
|
||||||
|
var coords = this.index2coord(this.active_cells[i]);
|
||||||
|
var x = coords[1];
|
||||||
|
var y = coords[0];
|
||||||
|
|
||||||
|
this.grid.cells[y][x].activate();
|
||||||
|
}
|
||||||
|
var time = this.level * 300 + 2000;
|
||||||
|
setTimeout(c => this.end_show_sequence(c), time);
|
||||||
|
}
|
||||||
|
|
||||||
|
end_show_sequence()
|
||||||
|
{
|
||||||
|
this.status_callback("it's your turn, reproduce the sequence!");
|
||||||
|
this.sidebar_div.style.backgroundColor = "rgba(0,0,0," + this.default_opacity + ")";
|
||||||
|
this.grid.deactivate_all();
|
||||||
|
this.unblock();
|
||||||
|
}
|
||||||
|
|
||||||
|
on_level_up()
|
||||||
|
{
|
||||||
|
if (this.level >= this.grid.width * this.grid.height)
|
||||||
|
{
|
||||||
|
this.on_win();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.block();
|
||||||
|
this.sidebar_div.style.backgroundColor = "rgba(0,255,0," + this.default_opacity + ")";
|
||||||
|
this.level++;
|
||||||
|
this.n_selected = 0;
|
||||||
|
this.level_callback(this.level);
|
||||||
|
this.create_new_sequence();
|
||||||
|
this.show_sequence();
|
||||||
|
}
|
||||||
|
|
||||||
|
on_game_over()
|
||||||
|
{
|
||||||
|
// activate all correct cells:
|
||||||
|
this.grid.deactivate_all();
|
||||||
|
var i;
|
||||||
|
for (i=0; i < this.active_cells.length; i++)
|
||||||
|
{
|
||||||
|
var coords = this.index2coord(this.active_cells[i]);
|
||||||
|
var x = coords[1];
|
||||||
|
var y = coords[0];
|
||||||
|
|
||||||
|
this.grid.cells[y][x].activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.status_callback("Game Over. Won rounds: " + (this.level - 1));
|
||||||
|
this.sidebar_div.style.backgroundColor = "rgba(255,0,0," + this.default_opacity + ")";
|
||||||
|
this.level = 0;
|
||||||
|
this.n_selected = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
on_win()
|
||||||
|
{
|
||||||
|
this.status_callback("Congratulation, You Won");
|
||||||
|
this.sidebar_div.style.backgroundColor = "rgba(0,255,0," + this.default_opacity + ")";
|
||||||
|
this.level = 0;
|
||||||
|
this.n_selected = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
block()
|
||||||
|
{
|
||||||
|
this.blocked = true;
|
||||||
|
this.grid.block();
|
||||||
|
}
|
||||||
|
|
||||||
|
unblock()
|
||||||
|
{
|
||||||
|
this.grid.unblock();
|
||||||
|
this.blocked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
index2coord(i)
|
||||||
|
{
|
||||||
|
var x = i % this.grid.width;
|
||||||
|
var y = Math.floor(i / this.grid.width);
|
||||||
|
var result = [y,x];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
coords2index(y,x)
|
||||||
|
{
|
||||||
|
return y * this.grid.width + x;
|
||||||
|
}
|
||||||
|
}
|
97
grid.js
Normal file
97
grid.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
class Grid
|
||||||
|
{
|
||||||
|
constructor(width, height, grid_container_div, tile_width, tile_height)
|
||||||
|
{
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
|
||||||
|
this.tile_width = tile_width;
|
||||||
|
this.tile_height = tile_height;
|
||||||
|
|
||||||
|
this.cells = []
|
||||||
|
|
||||||
|
this.grid_container_div = grid_container_div
|
||||||
|
|
||||||
|
this.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
create()
|
||||||
|
{
|
||||||
|
var x,y;
|
||||||
|
for (y = 0; y < this.height; y++)
|
||||||
|
{
|
||||||
|
var div_row = this.grid_container_div.appendChild(document.createElement("div"));
|
||||||
|
var row = [];
|
||||||
|
for (x = 0; x < this.width; x++)
|
||||||
|
{
|
||||||
|
var div_button = div_row.appendChild(document.createElement("div"));
|
||||||
|
div_button.className = "grid-tile";
|
||||||
|
var button = document.createElement("button");
|
||||||
|
button.className = "grid-button";
|
||||||
|
div_button.appendChild(button);
|
||||||
|
var c = "rgb(" + Math.round(255 * x / this.width) + ", " + Math.round(255 * y / this.height) + ", " + Math.round(255 * ( 1.0 - x / this.width))+")";
|
||||||
|
row.push(new Tile(x,y,this.tile_width,this.tile_height,button,c,div_button));
|
||||||
|
row[x].register_click_callback((i,j) => this.click_listener(i,j));
|
||||||
|
}
|
||||||
|
this.cells.push(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activate_all()
|
||||||
|
{
|
||||||
|
var x,y;
|
||||||
|
for (y = 0; y < this.height; y++)
|
||||||
|
{
|
||||||
|
for (x = 0; x < this.width; x++)
|
||||||
|
{
|
||||||
|
this.cells[y][x].activate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deactivate_all()
|
||||||
|
{
|
||||||
|
var x,y;
|
||||||
|
for (y = 0; y < this.height; y++)
|
||||||
|
{
|
||||||
|
for (x = 0; x < this.width; x++)
|
||||||
|
{
|
||||||
|
this.cells[y][x].deactivate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register_click_callback(func)
|
||||||
|
{
|
||||||
|
this.click_callback = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
click_listener(x,y)
|
||||||
|
{
|
||||||
|
this.click_callback(x,y);
|
||||||
|
}
|
||||||
|
|
||||||
|
block()
|
||||||
|
{
|
||||||
|
var x,y;
|
||||||
|
for (y = 0; y < this.height; y++)
|
||||||
|
{
|
||||||
|
for (x = 0; x < this.width; x++)
|
||||||
|
{
|
||||||
|
this.cells[y][x].lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unblock()
|
||||||
|
{
|
||||||
|
var x,y;
|
||||||
|
for (y = 0; y < this.height; y++)
|
||||||
|
{
|
||||||
|
for (x = 0; x < this.width; x++)
|
||||||
|
{
|
||||||
|
this.cells[y][x].unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
index.html
Normal file
22
index.html
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<html>
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css">
|
||||||
|
<head>
|
||||||
|
<title>Simon Says</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- <canvas id="myCanvas"></canvas> -->
|
||||||
|
<div id="main-container" class="main-container">
|
||||||
|
<div id="sidebar-container" class="sidebar-container">
|
||||||
|
<div id="control-container" class="control-container"></div>
|
||||||
|
<div id="info-container" class="info-container"></div>
|
||||||
|
</div>
|
||||||
|
<div id="grid-container" class="grid-container"></div>
|
||||||
|
</div>
|
||||||
|
<script src="tile.js"></script>
|
||||||
|
<script src="grid.js"></script>
|
||||||
|
<script src="game_manager.js"></script>
|
||||||
|
<script src="sidebar.js"></script>
|
||||||
|
<script src="main.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
10
main.js
Normal file
10
main.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
var width = getComputedStyle(document.body).getPropertyValue("--grid-width");
|
||||||
|
var height = getComputedStyle(document.body).getPropertyValue("--grid-height");
|
||||||
|
var tilesize = parseInt(getComputedStyle(document.body).getPropertyValue("--tile-size"));
|
||||||
|
var default_opacity = getComputedStyle(document.body).getPropertyValue("--opacity")
|
||||||
|
|
||||||
|
var grid = new Grid(width,height, document.getElementById("grid-container"), tilesize, tilesize);
|
||||||
|
var game_manager = new GameManager(grid, document.getElementById("sidebar-container"), default_opacity);
|
||||||
|
var sidebar = new Sidebar(grid, document.getElementById("control-container"), document.getElementById("info-container"), game_manager);
|
53
sidebar.js
Normal file
53
sidebar.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
class Sidebar
|
||||||
|
{
|
||||||
|
constructor(grid, control_container, info_container, game_manager)
|
||||||
|
{
|
||||||
|
this.grid = grid;
|
||||||
|
this.control_container = control_container;
|
||||||
|
this.info_container = info_container
|
||||||
|
this.game_manager = game_manager;
|
||||||
|
|
||||||
|
this.build_controls();
|
||||||
|
this.game_manager.register_level_callback(c => this.level_callback_listener(c));
|
||||||
|
this.game_manager.register_statustext_callback(c => this.status_callback_listener(c));
|
||||||
|
this.bind_events();
|
||||||
|
}
|
||||||
|
|
||||||
|
build_controls()
|
||||||
|
{
|
||||||
|
this.b_start = document.createElement("button");
|
||||||
|
this.b_start.className = "control-button";
|
||||||
|
this.b_start.appendChild(document.createTextNode("Start"))
|
||||||
|
|
||||||
|
this.control_container.appendChild(this.b_start);
|
||||||
|
|
||||||
|
this.b_score = document.createElement("button");
|
||||||
|
this.b_score.className = "info-button";
|
||||||
|
this.t_scoretext = document.createTextNode("Round: ");
|
||||||
|
this.b_score.appendChild(this.t_scoretext);
|
||||||
|
|
||||||
|
this.b_status = document.createElement("button");
|
||||||
|
this.b_status.className = "status-button";
|
||||||
|
this.t_statustext = document.createTextNode("");
|
||||||
|
this.b_status.appendChild(this.t_statustext);
|
||||||
|
|
||||||
|
this.control_container.appendChild(this.b_start);
|
||||||
|
this.info_container.appendChild(this.b_score);
|
||||||
|
this.info_container.appendChild(this.b_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind_events()
|
||||||
|
{
|
||||||
|
this.b_start.addEventListener("click", c => this.game_manager.restart(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
level_callback_listener(level)
|
||||||
|
{
|
||||||
|
this.t_scoretext.nodeValue = "Round: " + level;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_callback_listener(text)
|
||||||
|
{
|
||||||
|
this.t_statustext.nodeValue = text;
|
||||||
|
}
|
||||||
|
}
|
131
style.css
Normal file
131
style.css
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
canvas { background: #eee; width: 100%; height: 100%; }
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
background: rgb(49, 51, 54);
|
||||||
|
padding: 100;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root{
|
||||||
|
--grid-width: 6;
|
||||||
|
--grid-height: 6;
|
||||||
|
--tile-size: 100px;
|
||||||
|
--button-margin: 5px;
|
||||||
|
--opacity: 0.164;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-button {
|
||||||
|
border-radius: var(--button-margin);
|
||||||
|
margin: var(--button-margin);
|
||||||
|
width: calc(var(--tile-size) - (var(--button-margin) * 2));
|
||||||
|
height: calc(var(--tile-size) - (var(--button-margin) * 2));
|
||||||
|
display: inline-block;
|
||||||
|
outline: 0;
|
||||||
|
transition-duration: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-button:focus {
|
||||||
|
outline: 0!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-button {
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 10px;
|
||||||
|
border: none;
|
||||||
|
display: list-item;
|
||||||
|
outline: 0;
|
||||||
|
transition-duration: 0.3s;
|
||||||
|
background: rgba(0, 0, 0, var(--opacity));
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
font-size: 30pt;
|
||||||
|
width: 256px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-button:hover {
|
||||||
|
background: rgba(255, 255, 255, var(--opacity));
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-button:active {
|
||||||
|
background: rgba(255, 255, 255, calc(var(--opacity) * 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-button {
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 10px;
|
||||||
|
border: none;
|
||||||
|
outline: 0;
|
||||||
|
transition-duration: 0.3s;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: none;
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
font-size: 30pt;
|
||||||
|
width: 256px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-button {
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 10px;
|
||||||
|
border: none;
|
||||||
|
outline: 0;
|
||||||
|
transition-duration: 0.3s;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: none;
|
||||||
|
color: rgb(128, 128, 128);
|
||||||
|
font-size: 20pt;
|
||||||
|
width: 256px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-tile {
|
||||||
|
position: absolute;
|
||||||
|
width: var(--tile-size);
|
||||||
|
height: var(--tile-size);
|
||||||
|
float: center;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-container {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-container {
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
margin: auto;
|
||||||
|
width: calc(var(--grid-width) * var(--tile-size));
|
||||||
|
height: calc(var(--grid-height) * var(--tile-size));
|
||||||
|
background: rgba(0, 0, 0, var(--opacity));
|
||||||
|
display: inline-block;
|
||||||
|
transition-duration: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-container {
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
vertical-align: top;
|
||||||
|
background: rgba(0, 0, 0, var(--opacity));
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-direction:column;
|
||||||
|
min-height: calc(var(--grid-height) * var(--tile-size));
|
||||||
|
position: relative;
|
||||||
|
transition-duration: 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-container {
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
top: 0;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-container {
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
bottom: 0;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align: center;
|
||||||
|
background: none;
|
||||||
|
}
|
98
tile.js
Normal file
98
tile.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
class Tile
|
||||||
|
{
|
||||||
|
constructor(x,y,w,h, elem, color, div)
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
|
||||||
|
this.w = w;
|
||||||
|
this.h = h;
|
||||||
|
|
||||||
|
// reference to element
|
||||||
|
this.elem = elem;
|
||||||
|
this.color = color;
|
||||||
|
this.div = div;
|
||||||
|
|
||||||
|
this.locked = false;
|
||||||
|
|
||||||
|
this.div.style.transform = "translate( " + (x * w) + "px, " + (y*h) + "px )";
|
||||||
|
this.reset();
|
||||||
|
this.bind();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
register_click_callback(func)
|
||||||
|
{
|
||||||
|
this.click_callback = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
bind()
|
||||||
|
{
|
||||||
|
this.elem.addEventListener("mouseenter", c => this.on_mouseenter(c));
|
||||||
|
this.elem.addEventListener("mouseleave", c => this.on_mouseleave(c));
|
||||||
|
this.elem.addEventListener("click", c => this.on_click(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
reset()
|
||||||
|
{
|
||||||
|
this.elem.style.backgroundColor = this.color;
|
||||||
|
this.elem.style.opacity = 1;
|
||||||
|
this.deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
lock()
|
||||||
|
{
|
||||||
|
this.locked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock()
|
||||||
|
{
|
||||||
|
this.locked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
deactivate()
|
||||||
|
{
|
||||||
|
this.is_activated = false;
|
||||||
|
this.elem.style.opacity = 0.3;
|
||||||
|
this.elem.style.border = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
activate()
|
||||||
|
{
|
||||||
|
this.is_activated = true;
|
||||||
|
this.elem.style.opacity = 1.0;
|
||||||
|
this.elem.style.border = "2px solid rgba(255,255,255,0.3)";
|
||||||
|
}
|
||||||
|
|
||||||
|
on_mouseenter()
|
||||||
|
{
|
||||||
|
if (!this.is_activated && !this.locked)
|
||||||
|
{
|
||||||
|
this.elem.style.opacity = 0.7;
|
||||||
|
this.elem.style.border = "1px solid rgba(255,255,255,0.3)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
on_mouseleave()
|
||||||
|
{
|
||||||
|
if (!this.is_activated && !this.locked)
|
||||||
|
{
|
||||||
|
this.elem.style.opacity = 0.3;
|
||||||
|
this.elem.style.border = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
on_click()
|
||||||
|
{
|
||||||
|
if (!this.locked)
|
||||||
|
{
|
||||||
|
var active_before = this.is_activated;
|
||||||
|
this.activate();
|
||||||
|
if (!active_before)
|
||||||
|
{
|
||||||
|
this.click_callback(this.x, this.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user