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 +3 -6
- data/VERSION +1 -1
- data/bin/tic_tac_toe +4 -1
- data/lib/tic_tac_toe.rb +50 -5
- data/spec/tic_tac_toe_spec.rb +51 -5
- data/tic_tac_toe.gemspec +2 -2
- metadata +4 -4
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.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
|
-
|
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
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
data/spec/tic_tac_toe_spec.rb
CHANGED
@@ -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
|
-
|
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.
|
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{
|
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:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 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:
|
18
|
+
date: 2011-01-02 00:00:00 +01:00
|
19
19
|
default_executable: tic_tac_toe
|
20
20
|
dependencies: []
|
21
21
|
|