diff --git a/game_manager.js b/game_manager.js new file mode 100644 index 0000000..6f726c4 --- /dev/null +++ b/game_manager.js @@ -0,0 +1,11 @@ +class GameManager +{ + constructor(grid) + { + this.grid = grid; + + this.dummy_player = new Player("test_player", "rgb(0,128,0)"); + + this.grid.player_change_listener(this.dummy_player); + } +} \ No newline at end of file diff --git a/grid.js b/grid.js new file mode 100644 index 0000000..5301866 --- /dev/null +++ b/grid.js @@ -0,0 +1,124 @@ +class Grid +{ + constructor(n, grid_container_div, tile_width, tile_height, ground_color) + { + this.n = n; + this.grid_container_div = grid_container_div; + this.tile_width = tile_width; + this.tile_height = tile_height; + this.ground_color = ground_color; + + this.subgrids = [] + + console.log("create grid of size " + this.n); + + this.create(); + } + + create() + { + var x,y; + for (y = 0; y < this.n; y++) + { + var div_row = this.grid_container_div.appendChild(document.createElement("div")); + var row = []; + for (x = 0; x < this.n; x++) + { + var div_subgrid = div_row.appendChild(document.createElement("div")); + div_subgrid.className = "subgrid-container"; + + row.push(new Subgrid(this.n, x,y,div_subgrid,this.tile_width, this.tile_height, this.ground_color)); + row[x].register_click_callback((i,j,k,l) => this.click_listener(i,j,k,l)); + + } + + this.subgrids.push(row); + } + } + + register_click_callback(func) + { + this.click_callback = func; + } + + + click_listener(sub_x, sub_y, x,y) + { + this.click_callback(sub_x, sub_y, x,y); + } + + player_change_listener(player) + { + var x,y; + for (y = 0; y < this.n; y++) + { + for (x = 0; x < this.n; x++) + { + this.subgrids[y][x].player_change_listener(player); + } + } + } + + activate_all() + { + var x,y; + for (y = 0; y < this.n; y++) + { + for (x = 0; x < this.n; x++) + { + this.subgrids[y][x].activate_all(); + } + } + } + + deactivate_all() + { + var x,y; + for (y = 0; y < this.n; y++) + { + for (x = 0; x < this.n; x++) + { + this.subgrids[y][x].deactivate_all(); + } + } + } + + block() + { + var x,y; + for (y = 0; y < this.n; y++) + { + for (x = 0; x < this.n; x++) + { + this.subgrids[y][x].block(); + } + } + } + + unblock() + { + var x,y; + for (y = 0; y < this.n; y++) + { + for (x = 0; x < this.n; x++) + { + this.subgrids[y][x].unblock(); + } + } + } + + on_screen_orientation_change(tile_w,tile_h) + { + this.tile_width = tile_w; + this.tile_h = tile_h; + + var x,y; + for (y = 0; y < this.n; y++) + { + for (x = 0; x < this.n; x++) + { + this.subgrids[y][x].on_screen_orientation_change(tile_w, tile_h); + } + } + } +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..6fd0013 --- /dev/null +++ b/index.html @@ -0,0 +1,27 @@ + + + + + + + + ultimate tictactoe + + +
+ + + + +
+
+ + + + + + + + + + \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..6334317 --- /dev/null +++ b/main.js @@ -0,0 +1,13 @@ +var style = getComputedStyle(document.body); +var n = style.getPropertyValue("--tictactoe_n"); +var tilesize = style.getPropertyValue("--tile-size"); +var default_opacity = style.getPropertyValue("--opacity"); +var ground_color = style.getPropertyValue("--ground-color"); + +var grid = new Grid(n, document.getElementById("grid-container"), tilesize, tilesize, ground_color); +var game_manager = new GameManager(grid); + +window.addEventListener("resize", function() { + var tilesize = getComputedStyle(document.body).getPropertyValue("--tile-size"); + grid.on_screen_orientation_change(tilesize, tilesize); +}) diff --git a/player.js b/player.js new file mode 100644 index 0000000..0c6f44b --- /dev/null +++ b/player.js @@ -0,0 +1,30 @@ +class Player +{ + constructor(name, color) + { + this.name = name; + this.color = color; + } + + set_name(name) + { + this.name = name; + } + + set_color(color) + { + this.color = color; + } + + get_name() + { + return this.name + } + + get_color() + { + return this.color; + } + + +} \ No newline at end of file diff --git a/sidebar.js b/sidebar.js new file mode 100644 index 0000000..e69de29 diff --git a/style.css b/style.css new file mode 100644 index 0000000..241bfbf --- /dev/null +++ b/style.css @@ -0,0 +1,95 @@ +html, body { + overflow-x: hidden; +} + +body { + margin: 0; + background: rgb(39, 34, 39); + outline: 0; + position: relative; +} + +:root{ + --tictactoe_n: 3; + --button-margin: 0.5vh; + --tile-size: calc((100vh / (var(--tictactoe_n) * var(--tictactoe_n))) - 2 * var(--button-margin)); + --opacity: 0.2; + --border-radius: 1vh; + --sidebar-width: 25vh; + --ground-color: rgb(58, 67, 73); + --board-size: calc(var(--tictactoe_n) * var(--tictactoe_n) * var(--tile-size) + var(--tictactoe_n) * var(--button-margin) * 4); + --sidebar-height: none; /* unused if not in portrait mode */ +} + +/* override settings on portrait mode: */ +@media (orientation: portrait) { + + :root{ + --button-margin: 0.5vw; + --tile-size: calc((100vw / (var(--tictactoe_n) * var(--tictactoe_n))) - 2 * var(--button-margin)); + --border-radius: 1vw; + --sidebar-width: 40vw; + --sidebar-height: 15vw; + } + +} + +.main-container { + text-align: center; + white-space: nowrap; + padding-top: calc(50vh - 0.5 * (var(--grid-height) * var(--tile-size)) - var(--border-radius)); +} + +/* override settings on portrait mode: */ +@media (orientation: portrait) { + + .main-container { + white-space: normal; + padding-top: calc(50vh - 0.5 * (var(--grid-height) * var(--tile-size) + 2 * var(--sidebar-height)) - 2 * var(--border-radius)); + } + +} + +.grid-tile { + position: absolute; + width: var(--tile-size); + height: var(--tile-size); + float: center; + background: none; +} + +.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.1s; +} + +.grid-button:focus { + outline: 0!important; +} + +.grid-container { + border-radius: var(--border-radius); + padding: var(--border-radius); + margin: auto; + width: calc(var(--board-size)); + height: calc(var(--board-size)); + background: rgba(0, 0, 0, var(--opacity)); + display: inline-block; + transition-duration: 0.5s; +} + +.subgrid-container { + border-radius: var(--border-radius); + padding: var(--border-radius); + margin: auto; + width: calc(var(--tictactoe_n) * var(--tile-size)); + height: calc(var(--tictactoe_n) * var(--tile-size)); + background: rgba(0, 0, 0, var(--opacity)); + display: inline-block; + transition-duration: 0.5s; +} diff --git a/subgrid.js b/subgrid.js new file mode 100644 index 0000000..41fb2e8 --- /dev/null +++ b/subgrid.js @@ -0,0 +1,144 @@ +class Subgrid +{ + constructor(n, sub_x, sub_y,subgrid_container_div, tile_width, tile_height, ground_color) + { + this.n = n; + this.subgrid_container_div = subgrid_container_div; + this.tile_height = tile_height; + this.tile_width = tile_width; + + this.cells = []; + + this.sub_x = sub_x; + this.sub_y = sub_y; + + this.ground_color = ground_color; + + this.create(); + + console.log("new subgrid created"); + } + + create() + { + var x,y; + for (y = 0; y < this.n; y++) + { + var div_row = this.subgrid_container_div.appendChild(document.createElement("div")); + var row = []; + for (x = 0; x < this.n; 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 b_x = x; + var b_y = y; + var b_w = this.tile_width; + var b_h = this.tile_height; + + row.push(new Tile(b_x, b_y, b_w, b_h, button, this.ground_color, div_button)); + // TODO: register listener? + row[x].register_click_callback((i,j) => this.click_listener(i,j)); + + } + this.cells.push(row); + } + } + + register_click_callback(func) + { + this.click_callback = func; + } + + + click_listener(x,y) + { + this.click_callback(this.sub_x, this.sub_y, x, y); + } + + player_change_listener(player) + { + var x,y; + for (y = 0; y < this.n; y++) + { + for (x = 0; x < this.n; x++) + { + this.cells[y][x].player_change_listener(player); + } + } + } + + unlock_request_listener(x,y) + { + // TODO: unused so far + + //return this.unlock_request_callback(x,y); + return false; + } + + activate_all() + { + var x,y; + for (y = 0; y < this.n; y++) + { + for (x = 0; x < this.n; x++) + { + this.cells[y][x].activate(); + } + } + } + + deactivate_all() + { + var x,y; + for (y = 0; y < this.n; y++) + { + for (x = 0; x < this.n; x++) + { + this.cells[y][x].deactivate(); + } + } + } + + block() + { + var x,y; + for (y = 0; y < this.n; y++) + { + for (x = 0; x < this.n; x++) + { + this.cells[y][x].lock(); + } + } + } + + unblock() + { + var x,y; + for (y = 0; y < this.n; y++) + { + for (x = 0; x < this.n; x++) + { + this.cells[y][x].unlock(); + } + } + } + + on_screen_orientation_change(tile_w,tile_h) + { + this.tile_width = tile_w; + this.tile_h = tile_h; + + var x,y; + for (y = 0; y < this.n; y++) + { + for (x = 0; x < this.n; x++) + { + this.cells[y][x].update_css_transform(tile_w, tile_h); + } + } + } +} \ No newline at end of file diff --git a/tile.js b/tile.js new file mode 100644 index 0000000..281d737 --- /dev/null +++ b/tile.js @@ -0,0 +1,130 @@ +class Tile +{ + constructor(x,y,w,h, elem, ground_color, div) + { + this.x = x; + this.y = y; + + this.w = w; + this.h = h; + + // reference to element + this.elem = elem; + this.ground_color = ground_color; + this.div = div; + + this.locked = false; + + this.div.style.transform = "translate( calc( " + x + "*" + w + "), calc(" + y + "*" + h + "))"; + + this.player = null; + this.reset(); + this.bind(); + + } + + update_css_transform(w,h) + { + this.w = w; + this.h = h; + this.div.style.transform = "translate( calc( " + this.x + "*" + this.w + "), calc(" + this.y + "*" + this.h + "))"; + } + + register_click_callback(func) + { + this.click_callback = func; + } + + register_unlock_request_callback(func) + { + this.unlock_request_callback = func; + } + + player_change_listener(player) + { + this.player = player; + } + + bind() + { + this.elem.addEventListener("mouseenter", c => this.on_mouseenter(c)); + this.elem.addEventListener("mouseleave", c => this.on_mouseleave(c)); + this.elem.addEventListener("mousedown", c => this.on_click(c)); + + // TODO: bind player listener + } + + reset() + { + this.elem.style.backgroundColor = this.ground_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.background = this.player.get_color(); + 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.background = this.player.get_color(); + 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.background = this.ground_color; + this.elem.style.opacity = 0.3; + this.elem.style.border = "none"; + } + } + + on_click() + { + if (this.locked) + { + // just check whether we can request an unlock. if not we're + // doing nothing! + if (!this.unlock_request_callback(this.x, this.y)) + { + return; + } + } + + var active_before = this.is_activated; + this.activate(); + if (!active_before) + { + this.click_callback(this.x, this.y); + } + + } + +} \ No newline at end of file