diff --git a/public/img/free.svg b/public/img/free.svg
new file mode 100644
index 0000000..f2873d3
--- /dev/null
+++ b/public/img/free.svg
@@ -0,0 +1,137 @@
+
+
+
+
\ No newline at end of file
diff --git a/public/img/o.svg b/public/img/o.svg
new file mode 100644
index 0000000..51f363e
--- /dev/null
+++ b/public/img/o.svg
@@ -0,0 +1,61 @@
+
+
+
+
diff --git a/public/img/thinking.svg b/public/img/thinking.svg
new file mode 100644
index 0000000..c991d35
--- /dev/null
+++ b/public/img/thinking.svg
@@ -0,0 +1,106 @@
+
+
+
diff --git a/public/img/x.svg b/public/img/x.svg
new file mode 100644
index 0000000..317bab9
--- /dev/null
+++ b/public/img/x.svg
@@ -0,0 +1,61 @@
+
+
+
+
diff --git a/public/index.html b/public/index.html
index de8ee73..bc1bee6 100644
--- a/public/index.html
+++ b/public/index.html
@@ -18,7 +18,7 @@
-
+
diff --git a/public/mttt.css b/public/mttt.css
index dc30815..a620565 100644
--- a/public/mttt.css
+++ b/public/mttt.css
@@ -1,3 +1,7 @@
-.mttt-cell:hover{
- background: lightblue;
+.debug-bar{
+ position: relative;
+ background: #fff;
+ display: flex;
+ justify-content: space-around;
+ z-index: 100;
}
\ No newline at end of file
diff --git a/src/game.nim b/src/game.nim
index ab1cfc9..fd47bf1 100644
--- a/src/game.nim
+++ b/src/game.nim
@@ -1,4 +1,4 @@
-import dom, strformat, times, jsconsole
+import dom, strformat, times, jsconsole, options, pure.strutils
import gamelight/[graphics, vec]
import libmttt
@@ -17,6 +17,12 @@ type
Scene {.pure.} = enum
MainMenu, Game
+ BoardSize = object ## Infos on size/position of a game board
+ x: int ## X position of the top left corner
+ y: int ## Y position of the top left corner
+ size: int ## Pixel size of the game board. (The board is assumed to be quadratic)
+ cellSize: int ## Pixel size of each cell in the board
+
const
gameBgColor = "#eaeaea"
metaBoardColor = "#484d4d"
@@ -29,45 +35,58 @@ var
# TODO this should be dynamic
sidebarWidth = 150 ## Size of the left sidebar
-proc cellClick(event: Event): void =
- ## TODO Process cell clicks from here
- let
- cX = event.target.getAttribute("x")
- cY = event.target.getAttribute("y")
- mX = event.target.getAttribute("metaX")
- mY = event.target.getAttribute("metaY")
- console.debug(fmt"Click: cell ({cX}, {cY}) in mini board ({mX}, {mY})")
+proc getGameArea(game: Game): BoardSize =
+ ## Get size and position of the game board on the canvas
+ let height = game.renderer.getHeight - 2 * padding
+ let width = game.renderer.getWidth - sidebarWidth - 3 * padding
+ result.x = 2 * padding + sidebarWidth
+ result.y = padding
+ result.size = min(height, width)
+ result.cellSize = (result.size / 3).toInt
-proc createMiniCells(renderer: Renderer2D, board: Coordinate, start: Point, size: float): seq[Element] =
+proc getCellInfo(e: Node): tuple[cell: Coordinate, board: Coordinate] =
+ ## Parse the game imformation from the HTML attributes of an element
+ var val: string
+ val = $e.getAttribute("x")
+ result.cell.x = val.parseInt
+ val = $e.getAttribute("y")
+ result.cell.y = val.parseInt
+ val = $e.getAttribute("metaX")
+ result.board.x = val.parseInt
+ val = $e.getAttribute("metaY")
+ result.board.y = val.parseInt
+
+proc cellClick(game: Game, event: Event): void =
+ ## TODO Process cell clicks from here
+ let (cell, board) = event.target.getCellInfo()
+ console.debug(fmt"Click: cell ({cell.x}, {cell.y}) in mini board ({board.x}, {board.y})")
+ if game.state.currentBoard.isNone:
+ discard game.state.makeMove(cell,board)
+ elif board == game.state.currentBoard.get:
+ discard game.state.makeMove(cell)
+
+proc createMiniCells(game: Game, board: Coordinate, start: Point, size: float): seq[Element] =
## Create the interactive cells in a miniboard
for x in 0 .. 2:
for y in 0 .. 2:
- let pos = (start.x + x * size, start.y + y * size).toPoint
- let cell: Element = renderer.createDivElement(pos, size, size)
+ let pos = ((start.x + x * size).toInt, (start.y + y * size).toInt).toPoint
+ let cell: Element = game.renderer.createDivElement(pos, size, size)
cell.setAttribute("info", fmt"({x}, {y}) at {start}")
cell.setAttribute("X", $x)
cell.setAttribute("Y", $y)
cell.setAttribute("metaX", $board.x)
cell.setAttribute("metaY", $board.y)
- cell.onclick = cellClick
cell.classList.add("mttt-cell")
- result.add cell
+ cell.addEventListener("click", proc(ev: Event) = cellClick(game, ev))
+ result.add cell
-proc createCellElements(renderer: Renderer2D): seq[Element] =
+proc createCellElements(game: Game): seq[Element] =
## Create the cell elements for the whole game board
- # TODO: put those somewhere shared
- let
- gAreaX = 2 * padding + sidebarWidth
- gAreaY = padding
- gAreaH = renderer.getHeight - 2 * padding
- gAreaW = renderer.getWidth - sidebarWidth - 3 * padding
- gSize = min(gAreaH, gAreaW)
- bSize = (gSize / 3)
-
+ let area = game.getGameArea()
for x in 0 .. 2:
for y in 0 .. 2:
- let pos = (gAreaX + x * bSize, gAreaY + y * bSize)
- result.add renderer.createMiniCells((x, y), pos, bSize / 3)
+ let pos = (area.x + x * area.cellSize, area.y + y * area.cellSize)
+ result.add game.createMiniCells((x, y), pos, area.cellSize / 3)
proc switchScene(game: Game, scene: Scene) =
case scene:
@@ -80,8 +99,7 @@ proc switchScene(game: Game, scene: Scene) =
let timePos = (padding.toFloat, padding.toFloat + 35.0).toPoint
game.timeElement = game.renderer.createTextElement("0", timePos, "#000000", 20, font)
- # TODO Save these somewhere???
- let elements = game.renderer.createCellElements()
+ discard game.createCellElements()
proc newGame*(canvasId: string, debug: bool = false): Game =
debugMode = debug
@@ -115,14 +133,8 @@ proc drawPos(renderer: Renderer2D, x, y: float): void =
proc drawPos(renderer: Renderer2D, point: Point): void =
renderer.drawPos(point.x.toFloat, point.y.toFloat)
-proc drawBoard(game: Game, zero: Point, size: int, color = "#000000", pad = 10): void =
- ## Draw a Tic Tac Toe board with the given sizes
- if (debugMode):
- game.renderer.drawPos(zero) # Top left corner
- game.renderer.drawPos((zero.x + size, zero.y).toPoint) # Top right corner
- game.renderer.drawPos((zero.x, zero.y + size).toPoint) # Bottom left corner
- game.renderer.drawPos((zero.x + size, zero.y + size).toPoint) # Bottom right corner
-
+proc drawBoardLines(game: Game, zero: Point, size: int, color = "#000000", pad = 10): void =
+ ## Draws the lines for a tic tac toe board on the canvas
for i in 1 .. 2:
let offX = ((size / 3) * i.toFloat) + zero.x.toFloat
let offY = ((size / 3) * i.toFloat) + zero.y.toFloat
@@ -143,28 +155,60 @@ proc drawBoard(game: Game, zero: Point, size: int, color = "#000000", pad = 10):
game.renderer.closePath()
game.renderer.strokePath(color, 3)
+proc drawMark(renderer: Renderer2D, pos: Point, size: int, mark: Mark) =
+ ## Draw a single mark on the canvas
+ case mark:
+ of Mark.Free:
+ if (debugMode):
+ renderer.drawImage("img/free.svg", pos, size, size, ImageAlignment.TopLeft)
+ of Mark.Player1:
+ renderer.drawImage("img/x.svg", pos, size, size, ImageAlignment.TopLeft)
+ of Mark.Player2:
+ renderer.drawImage("img/o.svg", pos, size, size, ImageAlignment.TopLeft)
+ else:
+ console.error(fmt"Invalid mark '{mark}'");
+
+proc drawPlayerMarks(game: Game, zero: Point, size: int, coord: Coordinate): void =
+ ## Draw the player marks for all cells in a board
+ let cellSize = (size / 3).toInt
+ for x in 0 .. 2:
+ for y in 0 .. 2:
+ let pos = ((zero.x + x * cellSize), (zero.y + y * cellSize)).toPoint ## Top left point for each cell in the board
+ let mark = game.state.board[coord.x][coord.y][x][y]
+ game.renderer.drawMark(pos, cellSize, mark)
+
+proc drawBoard(game: Game, zero: Point, size: int, coord: Coordinate, color = "#000000", pad = 10): void =
+ ## Draw a Tic Tac Toe board with the given sizes, if the game is still open.
+ ## If not, draw a big mark for the winner or something to show it is drawn
+ if (debugMode):
+ game.renderer.drawPos(zero) # Top left corner
+ game.renderer.drawPos((zero.x + size, zero.y).toPoint) # Top right corner
+ game.renderer.drawPos((zero.x, zero.y + size).toPoint) # Bottom left corner
+ game.renderer.drawPos((zero.x + size, zero.y + size).toPoint) # Bottom right corner
+
+ let status = game.state.board[coord.x][coord.y].checkBoard
+ if status == Mark.Free:
+ game.drawBoardLines(zero, size, color, pad)
+ game.drawPlayerMarks(zero, size, coord)
+ else:
+ game.renderer.drawMark(zero, size, status)
+
proc drawGame(game: Game) =
## Redraw the UI elements
game.timeElement.innerHTML = fmt"{game.totalTime.inMinutes}m {game.totalTime.inSeconds mod 60}s"
- let
- gAreaX = 2 * padding + sidebarWidth
- gAreay = padding
- gAreaH = game.renderer.getHeight - 2 * padding
- gAreaW = game.renderer.getWidth - sidebarWidth - 3 * padding
- gSize = min(gAreaH, gAreaW)
- bSize = (gSize / 3).toInt
+ let area = game.getGameArea()
# Draw a box araound the game board
- game.renderer.strokeRect(gAreaX, gAreaY, gSize, gSize, "#0000004d")
+ game.renderer.strokeRect(area.x, area.y, area.size, area.size, "#0000004d")
# Draw the meta board
- game.drawBoard((gAreaX, gAreaY).toPoint, gSize, metaBoardColor, 5)
+ game.drawBoardLines((area.x, area.y).toPoint, area.size, metaBoardColor, 5)
# Draw the small boards
for x in 0 .. 2:
for y in 0 .. 2:
- game.drawBoard((gAreaX + x*bSize, gAreaY + y*bSize).toPoint, bSize, miniBoardColor, 15)
+ game.drawBoard((area.x + x*area.cellSize, area.y + y*area.cellSize).toPoint, area.cellSize, (x, y), miniBoardColor, 15)
proc draw(game: Game) =
## Draw the current screen on the canvas