Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

add AI

  • Loading branch information...
commit 203b7050480245c24c84a1b7f80ab239174fc4b7 1 parent 1373a79
@grosser authored
View
9 Readme.md
@@ -1,4 +1,4 @@
-Play Tic-Tac-Toe using Curses(full-screen-commandline app)
+Play Tic-Tac-Toe using Curses(full-screen-commandline app) vs humans or AI
More of an example app.
@@ -10,11 +10,8 @@ Usage
=====
tic_tac_toe
-Use cursor keys to select field, enter to make your mark.
-
-TODO
-=====
- - AI
+Use cursor keys to select field, enter to make your mark.
+AI only plans 1 step ahead, so its beatable ;)
Author
======
View
5 bin/tic_tac_toe
@@ -24,13 +24,15 @@ def display(ttt)
write 0,0,ttt.board
if winner = ttt.winner
write(0, STATUS, "Player #{winner} has won!!!!")
+ elsif ttt.draw?
+ write(0, STATUS, "It is a draw...")
else
write(0, STATUS, "It is #{ttt.player}`s turn...")
end
end
init_screen do
- write(0, STATUS+1, "q=Quit r=Reset")
+ write(0, STATUS+1, "q=Quit r=Reset a=AI-move")
ttt = TicTacToe.new
@@ -42,6 +44,7 @@ init_screen do
when Curses::Key::DOWN then ttt.move(0,1)
when Curses::Key::RIGHT then ttt.move(1,0)
when Curses::Key::LEFT then ttt.move(-1,0)
+ when ?a then ttt.ai_move
when 10 then ttt.set # enter
when ?q then break
when ?r then ttt = TicTacToe.new
View
55 lib/tic_tac_toe.rb
@@ -49,7 +49,17 @@ def move(x,y)
def set
return if @fields[@position] != ' '
@fields[@position] = @player
- switch_player
+ @player = next_player
+ end
+
+ def ai_move
+ return unless position = ai_select_position
+ @position = position
+ set
+ end
+
+ def draw?
+ @fields.index(' ') == nil
end
def winner
@@ -69,9 +79,44 @@ def winner
private
- def switch_player
- current_player = PLAYERS.index(@player)
- next_player = (current_player + 1) % PLAYERS.size
- @player = PLAYERS[next_player]
+ def ai_select_position
+ return if winner or draw?
+ free_positions = self.class.array_indexes(@fields, ' ')
+
+ # how can i win ?
+ free_positions.detect{|position| can_by_taking?(position, @player) } or
+ # how can i prevent opponent from winning ?
+ free_positions.detect{|position| can_by_taking?(position, next_player) } or
+ # guess
+ free_positions.sort_by{rand}.first
+ end
+
+ def can_by_taking?(position, player)
+ taking(position, player) do
+ winner = self.winner
+ winner == player
+ end
+ end
+
+ def taking(position, player)
+ old_player, @player = @player, player
+ old_field = @fields[position]
+ @fields[position] = @player
+ yield
+ ensure
+ @fields[position] = old_field
+ @player = old_player
+ end
+
+ def self.array_indexes(array, find)
+ indexes = []
+ array.each_with_index{|item,i| indexes << i if find == item }
+ indexes
+ end
+
+ def next_player
+ current_index = PLAYERS.index(@player)
+ next_index = (current_index + 1) % PLAYERS.size
+ PLAYERS[next_index]
end
end
View
56 spec/tic_tac_toe_spec.rb
@@ -3,6 +3,18 @@
describe TicTacToe do
def ttt; subject; end
+ def set_fields(a)
+ ttt.instance_eval{@fields = a}
+ end
+
+ def get_fields
+ ttt.instance_eval{@fields}
+ end
+
+ def board_full_without_winner
+ set_fields ['O','X','O', 'O','X','O', 'X','O','X']
+ end
+
board_width = 14
it "has a VERSION" do
@@ -73,16 +85,12 @@ def ttt; subject; end
end
describe :winner do
- def set_fields(a)
- ttt.instance_eval{@fields = a}
- end
-
it "is nil when no-one has set" do
ttt.winner.should == nil
end
it "is nil when no-one has won" do
- set_fields ['O','X','O', 'O','X','O', 'X','O','X']
+ board_full_without_winner
ttt.winner.should == nil
end
@@ -106,4 +114,42 @@ def set_fields(a)
ttt.winner.should == 'O'
end
end
+
+ describe :ai do
+ it "does not move if winner is selected" do
+ set_fields(['O','O','O', ' ',' ',' ', ' ',' ',' '])
+ ttt.ai_move
+ ttt.player.should == 'X'
+ end
+
+ it "does not move if board is full" do
+ board_full_without_winner
+ ttt.ai_move
+ ttt.player.should == 'X'
+ end
+
+ it "makes a move" do
+ ttt.player.should == 'X'
+ ttt.ai_move
+ ttt.player.should == 'O'
+ end
+
+ it "prevents opponent from winning" do
+ set_fields(['O','O',' ', ' ',' ',' ', ' ',' ',' '])
+ ttt.ai_move
+ get_fields.should == ['O','O','X', ' ',' ',' ', ' ',' ',' ']
+ end
+
+ it "tries to win" do
+ set_fields(['O','O',' ', 'X','X',' ', ' ',' ',' '])
+ ttt.ai_move
+ get_fields.should == ['O','O',' ', 'X','X','X', ' ',' ',' ']
+ end
+
+ it "cannot prevent the inevitable" do
+ set_fields(['O','O',' ', 'O','O',' ', ' ',' ',' '])
+ ttt.ai_move
+ get_fields.should == ['O','O','X', 'O','O',' ', ' ',' ',' ']
+ end
+ end
end
Please sign in to comment.
Something went wrong with that request. Please try again.