# Copyright 2022 Mattia Giambirtone & All Contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import util/matrix type TileKind* = enum ## A tile enumeration kind Empty = 0, Self, Enemy GameStatus* = enum ## A game status enumeration Playing, Win, Lose, Draw TicTacToeGame* = ref object map*: Matrix[int] last: TileKind proc newTicTacToe*: TicTacToeGame = ## Creates a new TicTacToe object new(result) result.map = zeros[int]((3, 3)) proc get*(self: TicTacToeGame): GameStatus = ## Returns the game status # Checks the rows for _, row in self.map: if all(row == newMatrix[int](@[1, 1, 1])): return Win elif all(row == newMatrix[int](@[2, 2, 2])): return Lose # Checks the columns for _, col in self.map.transpose(): if all(col == newMatrix[int](@[1, 1, 1])): return Win elif all(col == newMatrix[int](@[2, 2, 2])): return Lose # Checks the diagonals if all(self.map.diag() == newMatrix[int](@[1, 1, 1])): return Win elif all(self.map.diag() == newMatrix[int](@[2, 2, 2])): return Lose # Top right diagonal (we flip the matrix left to right so # that the diagonal gets shifted on the other side) elif all(self.map.fliplr().diag() == newMatrix[int](@[1, 1, 1])): return Win elif all(self.map.fliplr().diag() == newMatrix[int](@[2, 2, 2])): return Lose # No check was successful and there's no empty slots: draw! if not any(self.map == 0): return Draw # There are empty slots and no one won yet, we're still in game! return Playing proc `$`*(self: TicTacToeGame): string = ## Stringifies self for i, row in self.map: for j, e in row: if e == 0: result &= "_" elif e == 1: result &= "X" else: result &= "O" if j in 0..8: result &= " " if i < 2: result &= "\n" proc place*(self: TicTacToeGame, tile: TileKind, x, y: int) = ## Places a tile onto the playing board if TileKind(self.map[x, y]) == Empty: self.map[x, y] = int(tile) proc asGame*(self: Matrix[int]): TicTacToeGame = ## Wraps a 3x3 matrix into a tris game ## object assert self.shape == (3, 3) new(result) result.map = self proc build*(data: seq[int]): TicTacToeGame = ## Builds a tris game from a flat ## sequence new(result) result.map = newMatrixFromSeq[int](data, (3, 3))