Invincible bot actually works
This commit is contained in:
parent
9376c1f4fb
commit
33d73dbf7b
68
board.rb
Normal file
68
board.rb
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
class Array
|
||||||
|
def same_values?
|
||||||
|
self.uniq.length == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def contains? x
|
||||||
|
self.include? x
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Board
|
||||||
|
|
||||||
|
attr_reader :spaces, :def_val
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@def_val = "---"
|
||||||
|
@spaces = Array.new(9, @def_val)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"|#{@spaces[0]}|#{@spaces[1]}|#{@spaces[2]}|
|
||||||
|
-------------
|
||||||
|
|#{@spaces[3]}|#{@spaces[4]}|#{@spaces[5]}|
|
||||||
|
-------------
|
||||||
|
|#{@spaces[6]}|#{@spaces[7]}|#{@spaces[8]}|
|
||||||
|
-------------"
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_free
|
||||||
|
@spaces.each_index.select { |s| @spaces[s] == @def_val }
|
||||||
|
end
|
||||||
|
|
||||||
|
def set val, idx
|
||||||
|
@spaces[idx] = val
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove idx
|
||||||
|
@spaces[idx] = @def_val
|
||||||
|
end
|
||||||
|
|
||||||
|
def any_winner?
|
||||||
|
winning_combos = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]
|
||||||
|
winning_combos.each do |s|
|
||||||
|
tmp = s.map {|i| @spaces[i]}
|
||||||
|
if (not tmp.include? @def_val) and tmp.same_values? then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_winner? player
|
||||||
|
winning_combos = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]
|
||||||
|
winning_combos.each do |s|
|
||||||
|
tmp = s.map {|i| @spaces[i]}
|
||||||
|
if (not tmp.include? @def_val) and tmp.same_values? and tmp[0] == player then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_full?
|
||||||
|
get_free.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
67
bot.rb
Normal file
67
bot.rb
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
class Bot
|
||||||
|
|
||||||
|
attr_reader :best_choice
|
||||||
|
|
||||||
|
def initialize board, piece
|
||||||
|
@piece = piece
|
||||||
|
@opponent = switch piece
|
||||||
|
end
|
||||||
|
|
||||||
|
def move board
|
||||||
|
return "Game is done!" if board.is_full?
|
||||||
|
return "Someone won!" if board.any_winner?
|
||||||
|
# move = @board.get_free.sample
|
||||||
|
# @board.set " O ", move
|
||||||
|
minmax board, @piece
|
||||||
|
board.set @piece, @best_choice
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def minmax board, cur_player
|
||||||
|
return score(board) if game_done?(board)
|
||||||
|
|
||||||
|
scores = {}
|
||||||
|
|
||||||
|
board.get_free.each do |space|
|
||||||
|
# Copy board so we don't mess up original
|
||||||
|
potential_board = board.dup
|
||||||
|
potential_board.set cur_player, space
|
||||||
|
|
||||||
|
|
||||||
|
scores[space] = minmax(potential_board, switch(cur_player))
|
||||||
|
potential_board.remove space
|
||||||
|
end
|
||||||
|
|
||||||
|
@best_choice, best_score = best_choice cur_player, scores
|
||||||
|
best_score
|
||||||
|
end
|
||||||
|
|
||||||
|
def switch cur
|
||||||
|
cur == " X " ? " O " : " X "
|
||||||
|
end
|
||||||
|
|
||||||
|
def game_done? board
|
||||||
|
board.any_winner? || board.is_full?
|
||||||
|
end
|
||||||
|
|
||||||
|
def best_choice piece, scores
|
||||||
|
if piece == @piece then
|
||||||
|
#p scores
|
||||||
|
scores.max_by { |_k, v| v}
|
||||||
|
else
|
||||||
|
scores.min_by { |_k, v| v}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def score board
|
||||||
|
#p @piece
|
||||||
|
#p @opponent
|
||||||
|
if board.is_winner? @piece
|
||||||
|
return 10
|
||||||
|
elsif board.is_winner? @opponent
|
||||||
|
return -10
|
||||||
|
end
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
21
human.rb
Normal file
21
human.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
class Human
|
||||||
|
|
||||||
|
attr_reader :piece
|
||||||
|
|
||||||
|
def initialize board, piece
|
||||||
|
@board = board
|
||||||
|
@piece = piece
|
||||||
|
end
|
||||||
|
|
||||||
|
def move
|
||||||
|
print "\nPick a move!\n"
|
||||||
|
free_moves = @board.get_free
|
||||||
|
idx = gets.strip.to_i
|
||||||
|
if not free_moves.include? idx then
|
||||||
|
print "Square is already taken, try again!"
|
||||||
|
move
|
||||||
|
else
|
||||||
|
@board.set @piece, idx
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
153
ticcy.rb
153
ticcy.rb
|
@ -1,130 +1,47 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
DEF = " "
|
require './board'
|
||||||
PLAYS = [DEF, DEF, DEF, DEF, DEF, DEF, DEF, DEF, DEF]
|
require './bot'
|
||||||
SCENARIOS = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]
|
require './human'
|
||||||
COM = "COM"
|
|
||||||
COM_VAL = " O "
|
@board = Board.new
|
||||||
PLAYER = "PLAYER"
|
|
||||||
GAME_DONE = false
|
|
||||||
PLAYER_VAL = " X "
|
|
||||||
|
|
||||||
|
|
||||||
class Array
|
def play_game player, bot
|
||||||
def same_values?
|
|
||||||
self.uniq.length == 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_win board, player
|
|
||||||
cur_play = player == PLAYER ? PLAYER_VAL : COM_VAL
|
|
||||||
|
|
||||||
SCENARIOS.each do |s|
|
|
||||||
tmp = s.map {|i| board[i]}
|
|
||||||
# p board
|
|
||||||
if (not tmp.include? " ") and tmp.same_values? and tmp[0] == cur_play then
|
|
||||||
# p tmp
|
|
||||||
# p SCENARIOS
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def check_full board
|
|
||||||
if not board.include? " " then
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def draw_board board
|
|
||||||
return "
|
|
||||||
|#{board[0]}|#{board[1]}|#{board[2]}|
|
|
||||||
-------------
|
|
||||||
|#{board[3]}|#{board[4]}|#{board[5]}|
|
|
||||||
-------------
|
|
||||||
|#{board[6]}|#{board[7]}|#{board[8]}|
|
|
||||||
-------------"
|
|
||||||
end
|
|
||||||
|
|
||||||
def set player, idx
|
|
||||||
if player == PLAYER
|
|
||||||
PLAYS[idx] = PLAYER_VAL
|
|
||||||
elsif
|
|
||||||
PLAYS[idx] = COM_VAL
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def ai_turn board, player, depth=0
|
|
||||||
if (check_win board, player == PLAYER ? COM : PLAYER) then
|
|
||||||
return -10 + depth
|
|
||||||
end
|
|
||||||
|
|
||||||
if check_full board then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
max_seen = -(Float::INFINITY)
|
|
||||||
index = 0
|
|
||||||
value = player == PLAYER ? PLAYER_VAL : COM_VAL
|
|
||||||
|
|
||||||
(0...9).each do |x|
|
|
||||||
if board[x] == DEF then
|
|
||||||
new_board = board.dup
|
|
||||||
new_board[x] = value
|
|
||||||
lol = player == PLAYER ? COM : PLAYER
|
|
||||||
move_eval = -(ai_turn new_board, lol, depth+1)
|
|
||||||
|
|
||||||
if move_eval > max_seen then
|
|
||||||
# p [move_eval, x]
|
|
||||||
max_seen = move_eval
|
|
||||||
index = x
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if depth == 0 then
|
|
||||||
set COM, index
|
|
||||||
end
|
|
||||||
return max_seen
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def play_game
|
|
||||||
while true do
|
while true do
|
||||||
# system "clear"
|
# system "clear"
|
||||||
|
|
||||||
# if (check_win PLAYS, PLAYER) == true then
|
print @board.to_s
|
||||||
# print "We have a winner!\n"
|
player.move
|
||||||
# break
|
|
||||||
# end
|
|
||||||
# if check_full board then
|
|
||||||
# print "Game over!\n"
|
|
||||||
# break
|
|
||||||
# end
|
|
||||||
|
|
||||||
print draw_board PLAYS
|
if @board.is_full? then
|
||||||
puts "\nPick one between 0-8"
|
print @board.to_s
|
||||||
choice = gets
|
print "Game is done!\n"
|
||||||
|
break
|
||||||
if PLAYS[choice.to_i] != " " then
|
end
|
||||||
print "Space is already taken"
|
|
||||||
else
|
if @board.any_winner? then
|
||||||
set PLAYER, choice.to_i
|
print @board.to_s
|
||||||
ai_turn PLAYS, COM
|
print "Someone won!\n"
|
||||||
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
|
bot.move @board
|
||||||
|
|
||||||
|
if @board.is_full? then
|
||||||
|
print @board.to_s
|
||||||
|
print "Game is done!\n"
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if @board.any_winner? then
|
||||||
|
print @board.to_s
|
||||||
|
print "Someone won!\n"
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
play_game
|
player = Human.new @board, " X "
|
||||||
|
bot = Bot.new @board, " O "
|
||||||
|
play_game player, bot
|
||||||
|
|
Loading…
Reference in New Issue
Block a user