tic_tac_toe 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Readme.md CHANGED
@@ -1,4 +1,4 @@
1
- Play Tic-Tac-Toe using Curses(full-screen-commandline app)
1
+ Play Tic-Tac-Toe using Curses(full-screen-commandline app) vs humans or AI
2
2
  More of an example app.
3
3
 
4
4
 
@@ -10,11 +10,8 @@ Usage
10
10
  =====
11
11
  tic_tac_toe
12
12
 
13
- Use cursor keys to select field, enter to make your mark.
14
-
15
- TODO
16
- =====
17
- - AI
13
+ Use cursor keys to select field, enter to make your mark.
14
+ AI only plans 1 step ahead, so its beatable ;)
18
15
 
19
16
  Author
20
17
  ======
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
data/bin/tic_tac_toe CHANGED
@@ -24,13 +24,15 @@ def display(ttt)
24
24
  write 0,0,ttt.board
25
25
  if winner = ttt.winner
26
26
  write(0, STATUS, "Player #{winner} has won!!!!")
27
+ elsif ttt.draw?
28
+ write(0, STATUS, "It is a draw...")
27
29
  else
28
30
  write(0, STATUS, "It is #{ttt.player}`s turn...")
29
31
  end
30
32
  end
31
33
 
32
34
  init_screen do
33
- write(0, STATUS+1, "q=Quit r=Reset")
35
+ write(0, STATUS+1, "q=Quit r=Reset a=AI-move")
34
36
 
35
37
  ttt = TicTacToe.new
36
38
 
@@ -42,6 +44,7 @@ init_screen do
42
44
  when Curses::Key::DOWN then ttt.move(0,1)
43
45
  when Curses::Key::RIGHT then ttt.move(1,0)
44
46
  when Curses::Key::LEFT then ttt.move(-1,0)
47
+ when ?a then ttt.ai_move
45
48
  when 10 then ttt.set # enter
46
49
  when ?q then break
47
50
  when ?r then ttt = TicTacToe.new
data/lib/tic_tac_toe.rb CHANGED
@@ -49,7 +49,17 @@ BOARD
49
49
  def set
50
50
  return if @fields[@position] != ' '
51
51
  @fields[@position] = @player
52
- switch_player
52
+ @player = next_player
53
+ end
54
+
55
+ def ai_move
56
+ return unless position = ai_select_position
57
+ @position = position
58
+ set
59
+ end
60
+
61
+ def draw?
62
+ @fields.index(' ') == nil
53
63
  end
54
64
 
55
65
  def winner
@@ -69,9 +79,44 @@ BOARD
69
79
 
70
80
  private
71
81
 
72
- def switch_player
73
- current_player = PLAYERS.index(@player)
74
- next_player = (current_player + 1) % PLAYERS.size
75
- @player = PLAYERS[next_player]
82
+ def ai_select_position
83
+ return if winner or draw?
84
+ free_positions = self.class.array_indexes(@fields, ' ')
85
+
86
+ # how can i win ?
87
+ free_positions.detect{|position| can_by_taking?(position, @player) } or
88
+ # how can i prevent opponent from winning ?
89
+ free_positions.detect{|position| can_by_taking?(position, next_player) } or
90
+ # guess
91
+ free_positions.sort_by{rand}.first
92
+ end
93
+
94
+ def can_by_taking?(position, player)
95
+ taking(position, player) do
96
+ winner = self.winner
97
+ winner == player
98
+ end
99
+ end
100
+
101
+ def taking(position, player)
102
+ old_player, @player = @player, player
103
+ old_field = @fields[position]
104
+ @fields[position] = @player
105
+ yield
106
+ ensure
107
+ @fields[position] = old_field
108
+ @player = old_player
109
+ end
110
+
111
+ def self.array_indexes(array, find)
112
+ indexes = []
113
+ array.each_with_index{|item,i| indexes << i if find == item }
114
+ indexes
115
+ end
116
+
117
+ def next_player
118
+ current_index = PLAYERS.index(@player)
119
+ next_index = (current_index + 1) % PLAYERS.size
120
+ PLAYERS[next_index]
76
121
  end
77
122
  end
@@ -3,6 +3,18 @@ require File.expand_path('spec/spec_helper')
3
3
  describe TicTacToe do
4
4
  def ttt; subject; end
5
5
 
6
+ def set_fields(a)
7
+ ttt.instance_eval{@fields = a}
8
+ end
9
+
10
+ def get_fields
11
+ ttt.instance_eval{@fields}
12
+ end
13
+
14
+ def board_full_without_winner
15
+ set_fields ['O','X','O', 'O','X','O', 'X','O','X']
16
+ end
17
+
6
18
  board_width = 14
7
19
 
8
20
  it "has a VERSION" do
@@ -73,16 +85,12 @@ describe TicTacToe do
73
85
  end
74
86
 
75
87
  describe :winner do
76
- def set_fields(a)
77
- ttt.instance_eval{@fields = a}
78
- end
79
-
80
88
  it "is nil when no-one has set" do
81
89
  ttt.winner.should == nil
82
90
  end
83
91
 
84
92
  it "is nil when no-one has won" do
85
- set_fields ['O','X','O', 'O','X','O', 'X','O','X']
93
+ board_full_without_winner
86
94
  ttt.winner.should == nil
87
95
  end
88
96
 
@@ -106,4 +114,42 @@ describe TicTacToe do
106
114
  ttt.winner.should == 'O'
107
115
  end
108
116
  end
117
+
118
+ describe :ai do
119
+ it "does not move if winner is selected" do
120
+ set_fields(['O','O','O', ' ',' ',' ', ' ',' ',' '])
121
+ ttt.ai_move
122
+ ttt.player.should == 'X'
123
+ end
124
+
125
+ it "does not move if board is full" do
126
+ board_full_without_winner
127
+ ttt.ai_move
128
+ ttt.player.should == 'X'
129
+ end
130
+
131
+ it "makes a move" do
132
+ ttt.player.should == 'X'
133
+ ttt.ai_move
134
+ ttt.player.should == 'O'
135
+ end
136
+
137
+ it "prevents opponent from winning" do
138
+ set_fields(['O','O',' ', ' ',' ',' ', ' ',' ',' '])
139
+ ttt.ai_move
140
+ get_fields.should == ['O','O','X', ' ',' ',' ', ' ',' ',' ']
141
+ end
142
+
143
+ it "tries to win" do
144
+ set_fields(['O','O',' ', 'X','X',' ', ' ',' ',' '])
145
+ ttt.ai_move
146
+ get_fields.should == ['O','O',' ', 'X','X','X', ' ',' ',' ']
147
+ end
148
+
149
+ it "cannot prevent the inevitable" do
150
+ set_fields(['O','O',' ', 'O','O',' ', ' ',' ',' '])
151
+ ttt.ai_move
152
+ get_fields.should == ['O','O','X', 'O','O',' ', ' ',' ',' ']
153
+ end
154
+ end
109
155
  end
data/tic_tac_toe.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{tic_tac_toe}
8
- s.version = "0.1.0"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Michael Grosser"]
12
- s.date = %q{2010-12-31}
12
+ s.date = %q{2011-01-02}
13
13
  s.default_executable = %q{tic_tac_toe}
14
14
  s.email = %q{michael@grosser.it}
15
15
  s.executables = ["tic_tac_toe"]
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tic_tac_toe
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Michael Grosser
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-12-31 00:00:00 +01:00
18
+ date: 2011-01-02 00:00:00 +01:00
19
19
  default_executable: tic_tac_toe
20
20
  dependencies: []
21
21