ruby-tic-tac-toe-crazcalm 0.0.1
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.
- checksums.yaml +15 -0
- data/bin/TicTacToe.rb +364 -0
- data/bin/glade/TicTacToe.glade +309 -0
- data/bin/players.rb +64 -0
- data/bin/stack.rb +11 -0
- data/main.rb +14 -0
- data/tests/test_players.rb +40 -0
- data/tests/test_stack.rb +26 -0
- metadata +94 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NDZkYzYwZDJkYjFhNTNmOGRlYzBmY2JlN2U0ZTgwZTljODNkZjZjYw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NmQ2OWNiNzEwN2Y4NjUyNzdkZjZjZGIzZDYxYmJhZTJjOTZhYjM0ZA==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YWQ0YzY2OGU3MzkwN2ZjYWY4MzU5ZmViZWVlZTQzZDdmMDYxOGFjMzk2YzY4
|
10
|
+
MGJjOWFmNzk5YWJiYjJiMjBkYWNhZmM4ZGY3ZTk0MDAwMjMxNTk2YmNhZjAx
|
11
|
+
YmY5YTUzOWM4MjYzZjgwMTNkMDYzZDY2YjFhZjE5NmJmYmFkYzI=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OTNhNDc3ZDAyMzU5MzFiNTIxOWJlZDAxMWE1ZDQ5MzVjZmRhNmEyYjc4M2Iy
|
14
|
+
NGVhYzdmNzFiZjI2MTk3OWE3OTkxZTEyNjUzZDdlNzcxZWQ3ZGVkNWQyMjU4
|
15
|
+
MTNlNDU2MWQ3MDZkODNhYzgxZGJlZGY2M2UxZDgyYWUyN2E3MmM=
|
data/bin/TicTacToe.rb
ADDED
@@ -0,0 +1,364 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'players')
|
2
|
+
require File.join(File.dirname(__FILE__), 'stack')
|
3
|
+
|
4
|
+
class TicTacToe
|
5
|
+
|
6
|
+
include GladeGUI
|
7
|
+
|
8
|
+
DEFAULT_VALUE = " "
|
9
|
+
PLAYER1 = "Player One"
|
10
|
+
PLAYER2 = "Player Two"
|
11
|
+
TITLE = "Tic Tac Toe"
|
12
|
+
GAME_OVER = "Game Over"
|
13
|
+
ABOUT_PAGE = "\t Author: \tMarcus 'crazcalm' Willock\nSource Code: https://github.com/crazcalm/ruby-tic-tac-toe"
|
14
|
+
|
15
|
+
def before_show()
|
16
|
+
@player1 = Player.new("X", "Player1", false)
|
17
|
+
@player2 = Player.new("O", "Player2", true)
|
18
|
+
@stack = Stack.new
|
19
|
+
@builder["window1"].title = TITLE
|
20
|
+
@keys = [DEFAULT_VALUE] * 9
|
21
|
+
@builder["player_label"].label = @player1.name
|
22
|
+
@one_player_mode = true
|
23
|
+
@buttons = [
|
24
|
+
@builder["TicTacToe.keys[0]"],
|
25
|
+
@builder["TicTacToe.keys[1]"],
|
26
|
+
@builder["TicTacToe.keys[2]"],
|
27
|
+
@builder["TicTacToe.keys[3]"],
|
28
|
+
@builder["TicTacToe.keys[4]"],
|
29
|
+
@builder["TicTacToe.keys[5]"],
|
30
|
+
@builder["TicTacToe.keys[6]"],
|
31
|
+
@builder["TicTacToe.keys[7]"],
|
32
|
+
@builder["TicTacToe.keys[8]"]
|
33
|
+
]
|
34
|
+
|
35
|
+
# Methods used to set up the game
|
36
|
+
set_game_mode
|
37
|
+
initialize_players
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_game_mode
|
41
|
+
@one_player_mode = VR::Dialog.ok_box("'OK' == 1 player mode\n'Cancel' == 2 player mode")
|
42
|
+
end
|
43
|
+
|
44
|
+
def file_select_mode_activate(menuitem, data=nil)
|
45
|
+
set_game_mode
|
46
|
+
reset_board
|
47
|
+
initialize_players
|
48
|
+
if current_player_is?(@player2)
|
49
|
+
next_player
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize_players
|
54
|
+
if @one_player_mode
|
55
|
+
@player1, @player2 = players_factory("human", "bot")
|
56
|
+
else
|
57
|
+
@player1, @player2 = players_factory("human", "human")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def keys__clicked(button)
|
62
|
+
|
63
|
+
# game logic (High level)
|
64
|
+
if valid_move?(button) # Checks for valide move
|
65
|
+
make_move(button)
|
66
|
+
game_play_steps
|
67
|
+
else
|
68
|
+
try_again
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def game_play_steps
|
73
|
+
if is_over?
|
74
|
+
winner_is
|
75
|
+
game_over
|
76
|
+
elsif tie?
|
77
|
+
tie
|
78
|
+
else
|
79
|
+
next_player
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def make_move(button)
|
84
|
+
button.label = current_player_mark
|
85
|
+
@stack.push(button)
|
86
|
+
end
|
87
|
+
|
88
|
+
def _undo
|
89
|
+
if @stack.empty?
|
90
|
+
#VR::msg "Cannot undo move"
|
91
|
+
else
|
92
|
+
button = @stack.pop
|
93
|
+
button.label = DEFAULT_VALUE
|
94
|
+
#VR::msg "Move has been undone"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def file_undo_activate(menuitem, data=nil)
|
99
|
+
if @one_player_mode
|
100
|
+
_undo
|
101
|
+
_undo
|
102
|
+
else
|
103
|
+
_undo
|
104
|
+
unless @stack.empty?
|
105
|
+
next_player
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def current_player_is_a_bot?
|
111
|
+
@builder["player_label"].label == @player1.name ? @player1.bot : @player2.bot
|
112
|
+
end
|
113
|
+
|
114
|
+
def current_player_mark
|
115
|
+
@builder["player_label"].label == @player1.name ? @player1.move : @player2.move
|
116
|
+
end
|
117
|
+
|
118
|
+
def opponent_player_mark
|
119
|
+
@builder['player_label'].label == @player1.name ? @player2.move : @player1.move
|
120
|
+
end
|
121
|
+
|
122
|
+
def board_empty?
|
123
|
+
result = true
|
124
|
+
@buttons.each do |button|
|
125
|
+
unless button.label == DEFAULT_VALUE
|
126
|
+
result = false
|
127
|
+
end
|
128
|
+
end
|
129
|
+
result
|
130
|
+
end
|
131
|
+
|
132
|
+
def valid_move?(button)
|
133
|
+
result = button.label == DEFAULT_VALUE ? true : false
|
134
|
+
result
|
135
|
+
end
|
136
|
+
|
137
|
+
def current_player_is?(player)
|
138
|
+
@builder['player_label'].label == player.name ? true : false
|
139
|
+
end
|
140
|
+
|
141
|
+
def next_player
|
142
|
+
if @builder['player_label'].label == @player1.name
|
143
|
+
@builder['player_label'].label = @player2.name
|
144
|
+
else
|
145
|
+
@builder['player_label'].label = @player1.name
|
146
|
+
end
|
147
|
+
|
148
|
+
if current_player_is_a_bot?
|
149
|
+
bot_move
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def random_move
|
154
|
+
# options
|
155
|
+
numbers = (0...9).to_a
|
156
|
+
rand_num = numbers.sample
|
157
|
+
|
158
|
+
# Chooses a valid move
|
159
|
+
while not valid_move?(@buttons[rand_num]) do
|
160
|
+
#VR::msg "Button #{@buttons[rand_num]} num: #{rand_num}"
|
161
|
+
rand_num = numbers.sample
|
162
|
+
end
|
163
|
+
make_move(@buttons[rand_num])
|
164
|
+
end
|
165
|
+
|
166
|
+
def close_to_winning?(marker)
|
167
|
+
result = false
|
168
|
+
cases = board_cases
|
169
|
+
|
170
|
+
cases.each do |items|
|
171
|
+
|
172
|
+
testing = [items[0].label, items[1].label, items[2].label]
|
173
|
+
|
174
|
+
if testing.count(marker) == 2 && testing.count(DEFAULT_VALUE) == 1
|
175
|
+
result = true
|
176
|
+
return result
|
177
|
+
end
|
178
|
+
end
|
179
|
+
result
|
180
|
+
end
|
181
|
+
|
182
|
+
def opponent_is_close_to_winning
|
183
|
+
opponent_marker = opponent_player_mark
|
184
|
+
close_to_winning?(opponent_marker)
|
185
|
+
end
|
186
|
+
|
187
|
+
def winning_move
|
188
|
+
cases = board_cases
|
189
|
+
marker = current_player_mark
|
190
|
+
|
191
|
+
cases.each do |_items|
|
192
|
+
items = [_items[0].label, _items[1].label, _items[2].label]
|
193
|
+
if items.count(marker) == 2 && items.count(DEFAULT_VALUE) == 1
|
194
|
+
index = items.index(DEFAULT_VALUE)
|
195
|
+
make_move(_items[index])
|
196
|
+
break
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def stop_opponent_from_winning
|
202
|
+
# Iterate through the cases and and try to stop him/her from winning!
|
203
|
+
cases = board_cases
|
204
|
+
my_marker = current_player_mark
|
205
|
+
opponent_marker = opponent_player_mark
|
206
|
+
|
207
|
+
cases.each do |_items|
|
208
|
+
items = [_items[0].label, _items[1].label, _items[2].label]
|
209
|
+
if items.count(opponent_marker) == 2 && items.count(DEFAULT_VALUE) == 1
|
210
|
+
index = items.index(DEFAULT_VALUE)
|
211
|
+
make_move(_items[index])
|
212
|
+
break
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
def can_i_win?
|
219
|
+
my_marker = current_player_mark
|
220
|
+
close_to_winning?(my_marker)
|
221
|
+
end
|
222
|
+
|
223
|
+
def bot_move
|
224
|
+
|
225
|
+
#case1: Board is empty
|
226
|
+
if board_empty?
|
227
|
+
#VR::msg "Board is empty"
|
228
|
+
random_move
|
229
|
+
elsif can_i_win?
|
230
|
+
#VR::msg "Can I Win?"
|
231
|
+
winning_move
|
232
|
+
elsif opponent_is_close_to_winning # opponent has two in a row
|
233
|
+
#VR::msg "MY Oppenent is close to winning"
|
234
|
+
stop_opponent_from_winning
|
235
|
+
else
|
236
|
+
#VR::msg "The else case"
|
237
|
+
random_move
|
238
|
+
end
|
239
|
+
|
240
|
+
game_play_steps
|
241
|
+
end
|
242
|
+
|
243
|
+
def tie?
|
244
|
+
result = true
|
245
|
+
|
246
|
+
@buttons.each do |button|
|
247
|
+
result = false if button.label == DEFAULT_VALUE
|
248
|
+
end
|
249
|
+
|
250
|
+
result
|
251
|
+
end
|
252
|
+
|
253
|
+
def tie
|
254
|
+
VR::msg "Game has ended in a tie!"
|
255
|
+
game_over
|
256
|
+
end
|
257
|
+
|
258
|
+
def game_over
|
259
|
+
@builder["window1"].title = GAME_OVER
|
260
|
+
play_again?
|
261
|
+
end
|
262
|
+
|
263
|
+
# This method is needed to determine of the player would
|
264
|
+
# like to play again.
|
265
|
+
def play_again?
|
266
|
+
play_again = VR::Dialog.ok_box("Would you like to play again")
|
267
|
+
|
268
|
+
if play_again
|
269
|
+
reset_board
|
270
|
+
if current_player_is_a_bot?
|
271
|
+
bot_move
|
272
|
+
end
|
273
|
+
else
|
274
|
+
quit
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# This method is needed for when invalid moves are made and
|
279
|
+
# the same player must go again.
|
280
|
+
# I should add a mistake limit...
|
281
|
+
def try_again
|
282
|
+
VR::msg "That was in invalide move!"
|
283
|
+
end
|
284
|
+
|
285
|
+
def quit
|
286
|
+
#VR::msg "Qutting"
|
287
|
+
destroy_window
|
288
|
+
end
|
289
|
+
|
290
|
+
# need to add logic
|
291
|
+
def reset_board
|
292
|
+
@buttons.each do |button|
|
293
|
+
button.label = DEFAULT_VALUE
|
294
|
+
end
|
295
|
+
@builder["window1"].title = TITLE
|
296
|
+
end
|
297
|
+
|
298
|
+
def winner_is
|
299
|
+
VR::msg "The winner is #{@builder['player_label'].label}!"
|
300
|
+
end
|
301
|
+
|
302
|
+
def file_quit_activate(menuitem, data=nil)
|
303
|
+
quit
|
304
|
+
end
|
305
|
+
|
306
|
+
def file_about_activate(menuitem, data=nil)
|
307
|
+
VR::msg "#{ABOUT_PAGE}"
|
308
|
+
end
|
309
|
+
|
310
|
+
def board_cases
|
311
|
+
# Horizontal cases
|
312
|
+
case1 = @buttons[0..2]
|
313
|
+
case2 = @buttons[3..5]
|
314
|
+
case3 = @buttons[6..8]
|
315
|
+
|
316
|
+
# Vertical cases
|
317
|
+
case4 = [@buttons[0], @buttons[3], @buttons[6]]
|
318
|
+
case5 = [@buttons[1], @buttons[4], @buttons[7]]
|
319
|
+
case6 = [@buttons[2], @buttons[5], @buttons[8]]
|
320
|
+
|
321
|
+
# Diagonal cases
|
322
|
+
case7 = [@buttons[0], @buttons[4], @buttons[8]]
|
323
|
+
case8 = [@buttons[2], @buttons[4], @buttons[6]]
|
324
|
+
|
325
|
+
cases = [case1, case2, case3] +
|
326
|
+
[case4, case5, case6] +
|
327
|
+
[case7, case8]
|
328
|
+
|
329
|
+
cases
|
330
|
+
end
|
331
|
+
|
332
|
+
def is_over?
|
333
|
+
|
334
|
+
# This gets returned
|
335
|
+
result = false
|
336
|
+
|
337
|
+
cases = board_cases
|
338
|
+
|
339
|
+
cases.each do |items|
|
340
|
+
if items[0].label != DEFAULT_VALUE
|
341
|
+
if items[0].label == items[1].label && items[0].label == items[2].label
|
342
|
+
result = true
|
343
|
+
return result
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
result
|
348
|
+
end
|
349
|
+
|
350
|
+
def board_state
|
351
|
+
state = ""
|
352
|
+
@buttons.each_with_index do |item, index|
|
353
|
+
if (index + 1) % 3 == 0
|
354
|
+
state += " #{item.label}\n"
|
355
|
+
state += "---------------\n"
|
356
|
+
else
|
357
|
+
state += " #{item.label} | "
|
358
|
+
end
|
359
|
+
end
|
360
|
+
VR::msg state
|
361
|
+
end
|
362
|
+
|
363
|
+
end
|
364
|
+
|
@@ -0,0 +1,309 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<interface>
|
3
|
+
<requires lib="gtk+" version="2.16"/>
|
4
|
+
<!-- interface-naming-policy project-wide -->
|
5
|
+
<object class="GtkWindow" id="window1">
|
6
|
+
<property name="width_request">180</property>
|
7
|
+
<property name="height_request">220</property>
|
8
|
+
<property name="visible">True</property>
|
9
|
+
<property name="can_focus">False</property>
|
10
|
+
<property name="modal">True</property>
|
11
|
+
<property name="window_position">center-always</property>
|
12
|
+
<property name="default_width">300</property>
|
13
|
+
<property name="default_height">200</property>
|
14
|
+
<signal name="destroy" handler="destroy_window" swapped="no"/>
|
15
|
+
<child>
|
16
|
+
<object class="GtkVBox" id="vbox1">
|
17
|
+
<property name="visible">True</property>
|
18
|
+
<property name="can_focus">False</property>
|
19
|
+
<child>
|
20
|
+
<object class="GtkMenuBar" id="menubar1">
|
21
|
+
<property name="visible">True</property>
|
22
|
+
<property name="can_focus">False</property>
|
23
|
+
<property name="extension_events">cursor</property>
|
24
|
+
<child>
|
25
|
+
<object class="GtkMenuItem" id="menuitem1">
|
26
|
+
<property name="visible">True</property>
|
27
|
+
<property name="can_focus">False</property>
|
28
|
+
<property name="use_action_appearance">False</property>
|
29
|
+
<property name="label" translatable="yes">_File</property>
|
30
|
+
<property name="use_underline">True</property>
|
31
|
+
<child type="submenu">
|
32
|
+
<object class="GtkMenu" id="menu1">
|
33
|
+
<property name="visible">True</property>
|
34
|
+
<property name="can_focus">False</property>
|
35
|
+
<child>
|
36
|
+
<object class="GtkImageMenuItem" id="file_quit">
|
37
|
+
<property name="label">gtk-quit</property>
|
38
|
+
<property name="visible">True</property>
|
39
|
+
<property name="can_focus">False</property>
|
40
|
+
<property name="use_action_appearance">False</property>
|
41
|
+
<property name="use_underline">True</property>
|
42
|
+
<property name="use_stock">True</property>
|
43
|
+
<signal name="activate" handler="file_quit_activate" swapped="no"/>
|
44
|
+
</object>
|
45
|
+
</child>
|
46
|
+
<child>
|
47
|
+
<object class="GtkMenuItem" id="file_select_mode">
|
48
|
+
<property name="visible">True</property>
|
49
|
+
<property name="can_focus">False</property>
|
50
|
+
<property name="use_action_appearance">False</property>
|
51
|
+
<property name="label" translatable="yes">Select Mode</property>
|
52
|
+
<property name="use_underline">True</property>
|
53
|
+
<signal name="activate" handler="file_select_mode_activate" swapped="no"/>
|
54
|
+
</object>
|
55
|
+
</child>
|
56
|
+
</object>
|
57
|
+
</child>
|
58
|
+
</object>
|
59
|
+
</child>
|
60
|
+
<child>
|
61
|
+
<object class="GtkMenuItem" id="menuitem2">
|
62
|
+
<property name="visible">True</property>
|
63
|
+
<property name="can_focus">False</property>
|
64
|
+
<property name="use_action_appearance">False</property>
|
65
|
+
<property name="label" translatable="yes">_Edit</property>
|
66
|
+
<property name="use_underline">True</property>
|
67
|
+
<child type="submenu">
|
68
|
+
<object class="GtkMenu" id="menu2">
|
69
|
+
<property name="visible">True</property>
|
70
|
+
<property name="can_focus">False</property>
|
71
|
+
<child>
|
72
|
+
<object class="GtkImageMenuItem" id="file_undo">
|
73
|
+
<property name="label">gtk-undo</property>
|
74
|
+
<property name="visible">True</property>
|
75
|
+
<property name="can_focus">False</property>
|
76
|
+
<property name="use_action_appearance">False</property>
|
77
|
+
<property name="use_underline">True</property>
|
78
|
+
<property name="use_stock">True</property>
|
79
|
+
<signal name="activate" handler="file_undo_activate" swapped="no"/>
|
80
|
+
</object>
|
81
|
+
</child>
|
82
|
+
</object>
|
83
|
+
</child>
|
84
|
+
</object>
|
85
|
+
</child>
|
86
|
+
<child>
|
87
|
+
<object class="GtkMenuItem" id="menuitem4">
|
88
|
+
<property name="visible">True</property>
|
89
|
+
<property name="can_focus">False</property>
|
90
|
+
<property name="use_action_appearance">False</property>
|
91
|
+
<property name="label" translatable="yes">_Help</property>
|
92
|
+
<property name="use_underline">True</property>
|
93
|
+
<child type="submenu">
|
94
|
+
<object class="GtkMenu" id="menu3">
|
95
|
+
<property name="visible">True</property>
|
96
|
+
<property name="can_focus">False</property>
|
97
|
+
<child>
|
98
|
+
<object class="GtkImageMenuItem" id="file_about">
|
99
|
+
<property name="label">gtk-about</property>
|
100
|
+
<property name="visible">True</property>
|
101
|
+
<property name="can_focus">False</property>
|
102
|
+
<property name="use_action_appearance">False</property>
|
103
|
+
<property name="use_underline">True</property>
|
104
|
+
<property name="use_stock">True</property>
|
105
|
+
<signal name="activate" handler="file_about_activate" swapped="no"/>
|
106
|
+
</object>
|
107
|
+
</child>
|
108
|
+
</object>
|
109
|
+
</child>
|
110
|
+
</object>
|
111
|
+
</child>
|
112
|
+
</object>
|
113
|
+
<packing>
|
114
|
+
<property name="expand">False</property>
|
115
|
+
<property name="fill">True</property>
|
116
|
+
<property name="position">0</property>
|
117
|
+
</packing>
|
118
|
+
</child>
|
119
|
+
<child>
|
120
|
+
<object class="GtkTable" id="table1">
|
121
|
+
<property name="visible">True</property>
|
122
|
+
<property name="can_focus">False</property>
|
123
|
+
<property name="n_rows">3</property>
|
124
|
+
<property name="n_columns">3</property>
|
125
|
+
<child>
|
126
|
+
<object class="GtkButton" id="TicTacToe.keys[0]">
|
127
|
+
<property name="label" translatable="yes">button</property>
|
128
|
+
<property name="width_request">60</property>
|
129
|
+
<property name="height_request">60</property>
|
130
|
+
<property name="visible">True</property>
|
131
|
+
<property name="can_focus">True</property>
|
132
|
+
<property name="receives_default">True</property>
|
133
|
+
<property name="use_action_appearance">False</property>
|
134
|
+
</object>
|
135
|
+
</child>
|
136
|
+
<child>
|
137
|
+
<object class="GtkButton" id="TicTacToe.keys[1]">
|
138
|
+
<property name="label" translatable="yes">button</property>
|
139
|
+
<property name="width_request">60</property>
|
140
|
+
<property name="height_request">60</property>
|
141
|
+
<property name="visible">True</property>
|
142
|
+
<property name="can_focus">True</property>
|
143
|
+
<property name="receives_default">True</property>
|
144
|
+
<property name="use_action_appearance">False</property>
|
145
|
+
</object>
|
146
|
+
<packing>
|
147
|
+
<property name="left_attach">1</property>
|
148
|
+
<property name="right_attach">2</property>
|
149
|
+
</packing>
|
150
|
+
</child>
|
151
|
+
<child>
|
152
|
+
<object class="GtkButton" id="TicTacToe.keys[2]">
|
153
|
+
<property name="label" translatable="yes">button</property>
|
154
|
+
<property name="width_request">60</property>
|
155
|
+
<property name="height_request">60</property>
|
156
|
+
<property name="visible">True</property>
|
157
|
+
<property name="can_focus">True</property>
|
158
|
+
<property name="receives_default">True</property>
|
159
|
+
<property name="use_action_appearance">False</property>
|
160
|
+
</object>
|
161
|
+
<packing>
|
162
|
+
<property name="left_attach">2</property>
|
163
|
+
<property name="right_attach">3</property>
|
164
|
+
</packing>
|
165
|
+
</child>
|
166
|
+
<child>
|
167
|
+
<object class="GtkButton" id="TicTacToe.keys[3]">
|
168
|
+
<property name="label" translatable="yes">button</property>
|
169
|
+
<property name="width_request">60</property>
|
170
|
+
<property name="height_request">60</property>
|
171
|
+
<property name="visible">True</property>
|
172
|
+
<property name="can_focus">True</property>
|
173
|
+
<property name="receives_default">True</property>
|
174
|
+
<property name="use_action_appearance">False</property>
|
175
|
+
</object>
|
176
|
+
<packing>
|
177
|
+
<property name="top_attach">1</property>
|
178
|
+
<property name="bottom_attach">2</property>
|
179
|
+
</packing>
|
180
|
+
</child>
|
181
|
+
<child>
|
182
|
+
<object class="GtkButton" id="TicTacToe.keys[4]">
|
183
|
+
<property name="label" translatable="yes">button</property>
|
184
|
+
<property name="width_request">60</property>
|
185
|
+
<property name="height_request">60</property>
|
186
|
+
<property name="visible">True</property>
|
187
|
+
<property name="can_focus">True</property>
|
188
|
+
<property name="receives_default">True</property>
|
189
|
+
<property name="use_action_appearance">False</property>
|
190
|
+
</object>
|
191
|
+
<packing>
|
192
|
+
<property name="left_attach">1</property>
|
193
|
+
<property name="right_attach">2</property>
|
194
|
+
<property name="top_attach">1</property>
|
195
|
+
<property name="bottom_attach">2</property>
|
196
|
+
</packing>
|
197
|
+
</child>
|
198
|
+
<child>
|
199
|
+
<object class="GtkButton" id="TicTacToe.keys[5]">
|
200
|
+
<property name="label" translatable="yes">button</property>
|
201
|
+
<property name="width_request">60</property>
|
202
|
+
<property name="height_request">60</property>
|
203
|
+
<property name="visible">True</property>
|
204
|
+
<property name="can_focus">True</property>
|
205
|
+
<property name="receives_default">True</property>
|
206
|
+
<property name="use_action_appearance">False</property>
|
207
|
+
</object>
|
208
|
+
<packing>
|
209
|
+
<property name="left_attach">2</property>
|
210
|
+
<property name="right_attach">3</property>
|
211
|
+
<property name="top_attach">1</property>
|
212
|
+
<property name="bottom_attach">2</property>
|
213
|
+
</packing>
|
214
|
+
</child>
|
215
|
+
<child>
|
216
|
+
<object class="GtkButton" id="TicTacToe.keys[6]">
|
217
|
+
<property name="label" translatable="yes">button</property>
|
218
|
+
<property name="width_request">60</property>
|
219
|
+
<property name="height_request">60</property>
|
220
|
+
<property name="visible">True</property>
|
221
|
+
<property name="can_focus">True</property>
|
222
|
+
<property name="receives_default">True</property>
|
223
|
+
<property name="use_action_appearance">False</property>
|
224
|
+
</object>
|
225
|
+
<packing>
|
226
|
+
<property name="top_attach">2</property>
|
227
|
+
<property name="bottom_attach">3</property>
|
228
|
+
</packing>
|
229
|
+
</child>
|
230
|
+
<child>
|
231
|
+
<object class="GtkButton" id="TicTacToe.keys[7]">
|
232
|
+
<property name="label" translatable="yes">button</property>
|
233
|
+
<property name="width_request">60</property>
|
234
|
+
<property name="height_request">60</property>
|
235
|
+
<property name="visible">True</property>
|
236
|
+
<property name="can_focus">True</property>
|
237
|
+
<property name="receives_default">True</property>
|
238
|
+
<property name="use_action_appearance">False</property>
|
239
|
+
</object>
|
240
|
+
<packing>
|
241
|
+
<property name="left_attach">1</property>
|
242
|
+
<property name="right_attach">2</property>
|
243
|
+
<property name="top_attach">2</property>
|
244
|
+
<property name="bottom_attach">3</property>
|
245
|
+
</packing>
|
246
|
+
</child>
|
247
|
+
<child>
|
248
|
+
<object class="GtkButton" id="TicTacToe.keys[8]">
|
249
|
+
<property name="label" translatable="yes">button</property>
|
250
|
+
<property name="width_request">60</property>
|
251
|
+
<property name="height_request">60</property>
|
252
|
+
<property name="visible">True</property>
|
253
|
+
<property name="can_focus">True</property>
|
254
|
+
<property name="receives_default">True</property>
|
255
|
+
<property name="use_action_appearance">False</property>
|
256
|
+
</object>
|
257
|
+
<packing>
|
258
|
+
<property name="left_attach">2</property>
|
259
|
+
<property name="right_attach">3</property>
|
260
|
+
<property name="top_attach">2</property>
|
261
|
+
<property name="bottom_attach">3</property>
|
262
|
+
</packing>
|
263
|
+
</child>
|
264
|
+
</object>
|
265
|
+
<packing>
|
266
|
+
<property name="expand">True</property>
|
267
|
+
<property name="fill">True</property>
|
268
|
+
<property name="position">1</property>
|
269
|
+
</packing>
|
270
|
+
</child>
|
271
|
+
<child>
|
272
|
+
<object class="GtkHBox" id="hbox1">
|
273
|
+
<property name="visible">True</property>
|
274
|
+
<property name="can_focus">False</property>
|
275
|
+
<child>
|
276
|
+
<object class="GtkLabel" id="turn_label">
|
277
|
+
<property name="visible">True</property>
|
278
|
+
<property name="can_focus">False</property>
|
279
|
+
<property name="label" translatable="yes">Turn:</property>
|
280
|
+
</object>
|
281
|
+
<packing>
|
282
|
+
<property name="expand">True</property>
|
283
|
+
<property name="fill">True</property>
|
284
|
+
<property name="position">0</property>
|
285
|
+
</packing>
|
286
|
+
</child>
|
287
|
+
<child>
|
288
|
+
<object class="GtkLabel" id="player_label">
|
289
|
+
<property name="visible">True</property>
|
290
|
+
<property name="can_focus">False</property>
|
291
|
+
<property name="label" translatable="yes">Player</property>
|
292
|
+
</object>
|
293
|
+
<packing>
|
294
|
+
<property name="expand">True</property>
|
295
|
+
<property name="fill">True</property>
|
296
|
+
<property name="position">1</property>
|
297
|
+
</packing>
|
298
|
+
</child>
|
299
|
+
</object>
|
300
|
+
<packing>
|
301
|
+
<property name="expand">True</property>
|
302
|
+
<property name="fill">True</property>
|
303
|
+
<property name="position">2</property>
|
304
|
+
</packing>
|
305
|
+
</child>
|
306
|
+
</object>
|
307
|
+
</child>
|
308
|
+
</object>
|
309
|
+
</interface>
|
data/bin/players.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
##Notes
|
2
|
+
# I think that if your opponent is a bot, then the
|
3
|
+
# board should make the move for the bot.
|
4
|
+
# This makes sense because the board knows the
|
5
|
+
# the state of the board, whereas the bot
|
6
|
+
# Knows nothing about the board.
|
7
|
+
|
8
|
+
# Need to create a factory for Players
|
9
|
+
def players_factory(player1, player2, player1_marker_x=true)
|
10
|
+
players = []
|
11
|
+
|
12
|
+
# Cases of initializing the players
|
13
|
+
if player1_marker_x
|
14
|
+
|
15
|
+
# Case 1
|
16
|
+
if player1 == "human"
|
17
|
+
players << Player.new("X", "Player1", false)
|
18
|
+
else
|
19
|
+
players << Player.new("X", "Player1", true)
|
20
|
+
end
|
21
|
+
|
22
|
+
if player2 == "human"
|
23
|
+
players << Player.new("O", "Player2", false)
|
24
|
+
else
|
25
|
+
players << Player.new("O", "Player2", true)
|
26
|
+
end
|
27
|
+
|
28
|
+
else
|
29
|
+
# Case2
|
30
|
+
if player1 == "human"
|
31
|
+
players << Player.new("O", "Player1", false)
|
32
|
+
else
|
33
|
+
players << Player.new("O", "Player1", true)
|
34
|
+
end
|
35
|
+
|
36
|
+
if player2 == "human"
|
37
|
+
players << Player.new("X", "Player2", false)
|
38
|
+
else
|
39
|
+
players << Player.new("X", "Player2", true)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
players
|
44
|
+
end
|
45
|
+
|
46
|
+
class Player
|
47
|
+
attr_reader :marker, :bot
|
48
|
+
attr_accessor :try_limit, :name
|
49
|
+
|
50
|
+
def initialize(marker, name="Player1", bot=false)
|
51
|
+
@marker = marker
|
52
|
+
@try_limit = try_limit
|
53
|
+
@name = name
|
54
|
+
@bot = bot
|
55
|
+
end
|
56
|
+
|
57
|
+
def move
|
58
|
+
@marker
|
59
|
+
end
|
60
|
+
|
61
|
+
def invalid_move
|
62
|
+
@try_limit -=1
|
63
|
+
end
|
64
|
+
end
|
data/bin/stack.rb
ADDED
data/main.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'vrlib'
|
4
|
+
|
5
|
+
#make program output in real time so errors visible in VR.
|
6
|
+
STDOUT.sync = true
|
7
|
+
STDERR.sync = true
|
8
|
+
|
9
|
+
#everything in these directories will be included
|
10
|
+
my_path = File.expand_path(File.dirname(__FILE__))
|
11
|
+
require_all Dir.glob(my_path + "/bin/**/*.rb")
|
12
|
+
|
13
|
+
TicTacToe.new.show
|
14
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'bin', 'players')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
class PlayerTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@limit = 3
|
9
|
+
@name = "Marcus"
|
10
|
+
@marker = "X"
|
11
|
+
@player = Player.new(@marker, @limit, @name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_move
|
15
|
+
assert_equal(@marker, @player.move)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_try_limit
|
19
|
+
assert_equal(@limit, @player.try_limit)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_name
|
23
|
+
assert_equal(@name, @player.name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_invalid_move
|
27
|
+
@player.invalid_move
|
28
|
+
assert_equal(@limit - 1, @player.try_limit)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
class PlayerFactoryTest < Test::Unit::TestCase
|
34
|
+
|
35
|
+
def setup
|
36
|
+
end
|
37
|
+
|
38
|
+
def teardown
|
39
|
+
end
|
40
|
+
end
|
data/tests/test_stack.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'bin', 'stack')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
class StackTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@stack = Stack.new
|
9
|
+
@stack << 1
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_push
|
13
|
+
|
14
|
+
@stack.push(2)
|
15
|
+
assert_equal(@stack, [1,2])
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_pop
|
19
|
+
item = @stack.pop
|
20
|
+
assert_equal(item, 1)
|
21
|
+
end
|
22
|
+
|
23
|
+
def teardown
|
24
|
+
@stack.clear
|
25
|
+
end
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-tic-tac-toe-crazcalm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marcus Willock
|
8
|
+
autorequire:
|
9
|
+
bindir:
|
10
|
+
- .
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-08-25 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: vrlib
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ! '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 0.0.1
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ! '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 0.0.1
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: gtk2
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ! '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.0.1
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ! '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 0.0.1
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: require_all
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 0.0.1
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 0.0.1
|
56
|
+
description: Tic Tac Toe Game that allows for single player mode and 2 player mode.
|
57
|
+
email: craazcalm@gmail.com
|
58
|
+
executables:
|
59
|
+
- main.rb
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ./main.rb
|
64
|
+
- bin/TicTacToe.rb
|
65
|
+
- bin/glade/TicTacToe.glade
|
66
|
+
- bin/players.rb
|
67
|
+
- bin/stack.rb
|
68
|
+
- main.rb
|
69
|
+
- tests/test_players.rb
|
70
|
+
- tests/test_stack.rb
|
71
|
+
homepage: http://www.yoursite.org/
|
72
|
+
licenses: []
|
73
|
+
metadata: {}
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options: []
|
76
|
+
require_paths:
|
77
|
+
- .
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
requirements: []
|
89
|
+
rubyforge_project: nowarning
|
90
|
+
rubygems_version: 2.4.3
|
91
|
+
signing_key:
|
92
|
+
specification_version: 4
|
93
|
+
summary: Tic Tac Toe Clone
|
94
|
+
test_files: []
|