Minor changes and fixes
This commit is contained in:
parent
2da2ebed9a
commit
4f908c24fa
|
@ -16,7 +16,9 @@ import ../util/matrix
|
||||||
import../util/multibyte
|
import../util/multibyte
|
||||||
|
|
||||||
|
|
||||||
# import std/random
|
import std/random
|
||||||
|
|
||||||
|
randomize()
|
||||||
|
|
||||||
|
|
||||||
export matrix
|
export matrix
|
||||||
|
@ -152,7 +154,7 @@ proc get*(self: TicTacToeGame): GameStatus =
|
||||||
# a successful tic tac toe is 5, so we don't
|
# a successful tic tac toe is 5, so we don't
|
||||||
# bother checking if there's less than those
|
# bother checking if there's less than those
|
||||||
# in the current board
|
# in the current board
|
||||||
return
|
return Playing
|
||||||
# Checks the rows
|
# Checks the rows
|
||||||
for _, row in self.map:
|
for _, row in self.map:
|
||||||
if all(row == one):
|
if all(row == one):
|
||||||
|
@ -307,23 +309,23 @@ proc findBest*(tree: Move, map: Matrix[int]): Move =
|
||||||
]#
|
]#
|
||||||
|
|
||||||
|
|
||||||
proc findBest*(tree: Move, map: Matrix[uint8], maximize: bool = true, skip: int = 0): Choice =
|
proc findBest*(tree: Move, maximize: bool = true, skip: int = 0): Choice =
|
||||||
## Finds the best possible move in the
|
## Finds the best possible move in the
|
||||||
## given playing field using minimax
|
## given playing field using minimax
|
||||||
## tree search
|
## tree search. The first skip best
|
||||||
var start = tree.find(map)
|
## results (default 0) are skipped.
|
||||||
if start.outcome == WinX:
|
if tree.outcome == WinX:
|
||||||
return Choice(move: tree, weight: 10 - tree.depth)
|
return Choice(move: tree, weight: 10 - tree.depth)
|
||||||
elif start.outcome == WinO:
|
elif tree.outcome == WinO:
|
||||||
return Choice(move: tree, weight: -10 + tree.depth)
|
return Choice(move: tree, weight: -10 + tree.depth)
|
||||||
elif start.outcome == Draw:
|
elif tree.outcome == Draw:
|
||||||
return Choice(move: tree, weight: 0)
|
return Choice(move: tree, weight: 0)
|
||||||
var choices: seq[Choice] = @[]
|
var choices: seq[Choice] = @[]
|
||||||
for i in 0..8:
|
for i in 0..8:
|
||||||
if start.next[i].isNil():
|
if tree.next[i].isNil():
|
||||||
continue
|
continue
|
||||||
choices.add(start.next[i].findBest(start.next[i].state, maximize=not maximize))
|
choices.add(tree.next[i].findBest(maximize=not maximize))
|
||||||
choices[^1].move = start.next[i]
|
choices[^1].move = tree.next[i]
|
||||||
var best: Choice
|
var best: Choice
|
||||||
var bestWeight: int = 100
|
var bestWeight: int = 100
|
||||||
var worst: Choice
|
var worst: Choice
|
||||||
|
|
|
@ -27,11 +27,15 @@ import std/os
|
||||||
import std/exitprocs
|
import std/exitprocs
|
||||||
|
|
||||||
|
|
||||||
|
template clearScreen =
|
||||||
|
eraseScreen()
|
||||||
|
setCursorPos(0, 0)
|
||||||
|
|
||||||
|
|
||||||
proc play(treeA, treeB: Move) =
|
proc play(treeA, treeB: Move) =
|
||||||
## Plays a game of tic tac toe
|
## Plays a game of tic tac toe
|
||||||
## against the user
|
## against the user
|
||||||
eraseScreen()
|
clearScreen()
|
||||||
setCursorPos(0, 0)
|
|
||||||
var game = newTicTacToe()
|
var game = newTicTacToe()
|
||||||
var moves = treeA
|
var moves = treeA
|
||||||
var location: tuple[row, col: int]
|
var location: tuple[row, col: int]
|
||||||
|
@ -41,12 +45,10 @@ proc play(treeA, treeB: Move) =
|
||||||
moves = treeB
|
moves = treeB
|
||||||
location = where(moves.state, moves.state != game.map, 3).index(Self.uint8)
|
location = where(moves.state, moves.state != game.map, 3).index(Self.uint8)
|
||||||
game.place(Self, location.row, location.col)
|
game.place(Self, location.row, location.col)
|
||||||
eraseScreen()
|
clearScreen()
|
||||||
setCursorPos(0, 0)
|
|
||||||
styledEcho fgCyan, styleBright, "Computer chose ", fgYellow, $game.map.getIndex(location.row, location.col)
|
styledEcho fgCyan, styleBright, "Computer chose ", fgYellow, $game.map.getIndex(location.row, location.col)
|
||||||
else:
|
else:
|
||||||
eraseScreen()
|
clearScreen()
|
||||||
setCursorPos(0, 0)
|
|
||||||
while game.get() == Playing:
|
while game.get() == Playing:
|
||||||
styledEcho fgBlue, styleBright, "Tic Tac Bot v1.0"
|
styledEcho fgBlue, styleBright, "Tic Tac Bot v1.0"
|
||||||
echo game, "\n"
|
echo game, "\n"
|
||||||
|
@ -57,30 +59,25 @@ proc play(treeA, treeB: Move) =
|
||||||
index = int(parseBiggestInt(readLine(stdin).strip(chars={'\n'})))
|
index = int(parseBiggestInt(readLine(stdin).strip(chars={'\n'})))
|
||||||
location = ind2sub(index, game.map.shape)
|
location = ind2sub(index, game.map.shape)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
eraseScreen()
|
clearScreen()
|
||||||
setCursorPos(0, 0)
|
|
||||||
styledEcho fgRed, styleBright, "Invalid move"
|
styledEcho fgRed, styleBright, "Invalid move"
|
||||||
continue
|
continue
|
||||||
if index notin 0..8 or TileKind(game.map[location.row, location.col]) != Empty:
|
if index notin 0..8 or TileKind(game.map[location.row, location.col]) != Empty:
|
||||||
eraseScreen()
|
clearScreen()
|
||||||
setCursorPos(0, 0)
|
|
||||||
styledEcho fgRed, styleBright, "Invalid move"
|
styledEcho fgRed, styleBright, "Invalid move"
|
||||||
continue
|
continue
|
||||||
game.place(Enemy, location.row, location.col)
|
game.place(Enemy, location.row, location.col)
|
||||||
eraseScreen()
|
clearScreen()
|
||||||
setCursorPos(0, 0)
|
|
||||||
if game.get() == WinO:
|
if game.get() == WinO:
|
||||||
echo game, "\n"
|
echo game, "\n"
|
||||||
styledEcho fgGreen, styleBright, "Human wins!"
|
styledEcho fgGreen, styleBright, "Human wins!"
|
||||||
return
|
return
|
||||||
elif game.get() == Draw:
|
elif game.get() == Draw:
|
||||||
break
|
break
|
||||||
moves = moves.find(game.map)
|
moves = moves.next[game.map.getIndex(location.row, location.col)].findBest(true).move
|
||||||
moves = moves.findBest(game.map, true).move
|
|
||||||
location = where(moves.state, moves.state != game.map, 3).index(Self.uint8)
|
location = where(moves.state, moves.state != game.map, 3).index(Self.uint8)
|
||||||
game.place(Self, location.row, location.col)
|
game.place(Self, location.row, location.col)
|
||||||
eraseScreen()
|
clearScreen()
|
||||||
setCursorPos(0, 0)
|
|
||||||
if game.get() != Draw:
|
if game.get() != Draw:
|
||||||
styledEcho fgCyan, styleBright, "Computer chose ", fgYellow, $game.map.getIndex(location.row, location.col)
|
styledEcho fgCyan, styleBright, "Computer chose ", fgYellow, $game.map.getIndex(location.row, location.col)
|
||||||
if game.get() == WinX:
|
if game.get() == WinX:
|
||||||
|
@ -88,7 +85,7 @@ proc play(treeA, treeB: Move) =
|
||||||
styledEcho fgRed, styleBright, "Computer wins!"
|
styledEcho fgRed, styleBright, "Computer wins!"
|
||||||
return
|
return
|
||||||
echo game
|
echo game
|
||||||
echo "It's a draw!"
|
styledEcho fgYellow, styleBright, "It's a draw!"
|
||||||
|
|
||||||
|
|
||||||
proc hook {.noconv.} =
|
proc hook {.noconv.} =
|
||||||
|
@ -135,10 +132,9 @@ when isMainModule:
|
||||||
fp.close()
|
fp.close()
|
||||||
# Here we pick one of the first 5 best moves so that the bot doesn't
|
# Here we pick one of the first 5 best moves so that the bot doesn't
|
||||||
# always start with an X in the left corner when it's playing first
|
# always start with an X in the left corner when it's playing first
|
||||||
var state = movesB.state.copy()
|
|
||||||
var best: seq[Move] = @[]
|
var best: seq[Move] = @[]
|
||||||
for i in 0..4:
|
for i in 0..4:
|
||||||
best.add(movesB.findBest(state, true, i).move)
|
best.add(movesB.findBest(true, i).move)
|
||||||
movesB = sample(best)
|
movesB = sample(best)
|
||||||
while true:
|
while true:
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Reference in New Issue