tic_tac_toe 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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