tictactoe-randall 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,63 @@
1
+ <?xml version="1.0"?>
2
+ <interface>
3
+ <requires lib="gtk+" version="2.16"/>
4
+ <!-- interface-naming-policy project-wide -->
5
+ <object class="GtkWindow" id="window1">
6
+ <property name="visible">True</property>
7
+ <property name="title" translatable="yes">About Tic Tac Toe</property>
8
+ <property name="modal">True</property>
9
+ <property name="window_position">center</property>
10
+ <signal name="destroy" handler="destroy_window"/>
11
+ <child>
12
+ <object class="GtkHBox" id="hbox1">
13
+ <property name="visible">True</property>
14
+ <child>
15
+ <object class="GtkVBox" id="vbox1">
16
+ <property name="visible">True</property>
17
+ <child>
18
+ <object class="GtkLabel" id="label1">
19
+ <property name="visible">True</property>
20
+ <property name="label" translatable="yes">&lt;big&gt;&lt;big&gt;Tic Tac Toe 0.0.1&lt;/big&gt;&lt;/big&gt;
21
+ Created by Randall Reed in 2013 using VisualRuby, a Ruby GUI builder. To create your own Ruby GUIs, visit visualruby.net.
22
+ </property>
23
+ <property name="use_markup">True</property>
24
+ <property name="wrap">True</property>
25
+ </object>
26
+ <packing>
27
+ <property name="padding">20</property>
28
+ <property name="position">0</property>
29
+ </packing>
30
+ </child>
31
+ <child>
32
+ <object class="GtkHButtonBox" id="hbuttonbox1">
33
+ <property name="visible">True</property>
34
+ <child>
35
+ <object class="GtkButton" id="cancelButton">
36
+ <property name="label" translatable="yes">Ok</property>
37
+ <property name="visible">True</property>
38
+ <property name="can_focus">True</property>
39
+ <property name="receives_default">True</property>
40
+ <signal name="clicked" handler="destroy_window"/>
41
+ </object>
42
+ <packing>
43
+ <property name="expand">False</property>
44
+ <property name="fill">False</property>
45
+ <property name="position">0</property>
46
+ </packing>
47
+ </child>
48
+ </object>
49
+ <packing>
50
+ <property name="padding">10</property>
51
+ <property name="position">1</property>
52
+ </packing>
53
+ </child>
54
+ </object>
55
+ <packing>
56
+ <property name="padding">20</property>
57
+ <property name="position">0</property>
58
+ </packing>
59
+ </child>
60
+ </object>
61
+ </child>
62
+ </object>
63
+ </interface>
data/bin/o.jpg ADDED
Binary file
data/bin/player.rb ADDED
@@ -0,0 +1,32 @@
1
+ class Player
2
+ attr_reader :score
3
+ attr_reader :type
4
+ attr_reader :name
5
+ attr_reader :mark
6
+ def initialize(type, name, mark)
7
+ @name = name
8
+ @type = type
9
+ @score = 0
10
+ @mark = mark
11
+ end
12
+
13
+ def AddScore()
14
+ @score += 1
15
+ end
16
+
17
+ def ClearScore()
18
+ @score = 0
19
+ end
20
+
21
+ def SwapMark()
22
+ if @mark == 'X'
23
+ @mark = 'O'
24
+ else
25
+ @mark = 'X'
26
+ end
27
+ end
28
+
29
+ def Print()
30
+ print name + ", type: " + type + ", score: " + score.to_s
31
+ end
32
+ end
data/bin/tictactoe.rb ADDED
@@ -0,0 +1,492 @@
1
+ class TicTacToe
2
+
3
+ #Read access to some class variables
4
+ attr_reader :winner
5
+ attr_reader :movenum
6
+ attr_reader :player1
7
+ attr_reader :player2
8
+ attr_reader :cat
9
+ attr_reader :currentturn
10
+ attr_reader :difficulty
11
+
12
+ def initialize
13
+ #Set starting values for class variables
14
+ #@board = ['_','_','_','_','_','_','_','_','_']
15
+ @board = Array.new(9,'_')
16
+ #Keep track of current player
17
+ @currentturn = 'X'
18
+ @winner = ''
19
+ @movenum = 0
20
+ @lastmoveindex = -1
21
+ @penultimatemoveindex = -1
22
+ @difficulty = 'easy'
23
+ @movesuccess = false
24
+ @alternate = true
25
+ end
26
+
27
+ def Reset
28
+ #Reset game specific variables
29
+ @board = Array.new(9,'_')
30
+ @currentturn = 'X'
31
+ @winner = ''
32
+ @movenum = 0
33
+ @lastmoveindex = -1
34
+ @playagain = true
35
+ end
36
+
37
+ def PrintInstructions
38
+ if player1.mark == 'X'
39
+ return player1.name + ' (X) goes first'
40
+ else
41
+ return player2.name + ' (X) goes first'
42
+ end
43
+ end
44
+
45
+ def SelectPlayers(numplayers)
46
+ if @movenum == 0 or @winner != '' then
47
+ if numplayers == 1
48
+ @player1 = Player.new('human','Player','X')
49
+ @player2 = Player.new('computer','Computer','O')
50
+ @cat = Player.new('cat', 'Cat', 'C')
51
+ elsif numplayers == 2
52
+ @player1 = Player.new('human', 'Player 1','X')
53
+ @player2 = Player.new('human', 'Player 2','O')
54
+ @cat = Player.new('cat', 'Cat', 'C')
55
+ else
56
+ raise 'Invalid number of players: ' + numplayers
57
+ end
58
+ else
59
+ raise "Cannot change players during game"
60
+ end
61
+ end
62
+
63
+ def SwapPlayers
64
+ @player1.SwapMark()
65
+ @player2.SwapMark()
66
+ end
67
+
68
+ def UpdateScore(winner)
69
+ #Add a point to the winning player's score
70
+ if @player1.mark == winner
71
+ @player1.AddScore()
72
+ elsif @player2.mark == winner
73
+ @player2.AddScore()
74
+ else
75
+ @cat.AddScore()
76
+ end
77
+ end
78
+
79
+ def ClearScore
80
+ @player1.ClearScore
81
+ @player2.ClearScore
82
+ @cat.ClearScore
83
+ end
84
+
85
+ def SetDifficulty(difficulty)
86
+ if @movenum == 0 or @winner != ''
87
+ case difficulty.downcase
88
+ when 'easy'
89
+ @difficulty = 'easy'
90
+ when 'normal'
91
+ @difficulty = 'normal'
92
+ when 'hard'
93
+ @difficulty = 'hard'
94
+ else
95
+ raise 'Only valid difficulties are easy, medium, and hard'
96
+ end
97
+ end
98
+ end
99
+
100
+ #Print outcome of game to players
101
+ def PrintWinner
102
+ if @winner == 'C'
103
+ return "Sorry, cat game."
104
+ else
105
+ if @player1.mark == @winner
106
+ return "Congratulations! " + @player1.name + " wins!"
107
+ elsif @player2.mark == @winner and @player2.type == 'human'
108
+ return "Congratulations! " + @player2.name + " wins!"
109
+ elsif @player2.mark == @winner and @player2.type == 'computer'
110
+ #1 player, 'O' won. Do not congratulate player on computer victory.
111
+ return "Sorry, " + player2.name + " wins."
112
+ end
113
+ end
114
+ end
115
+
116
+ def SwapTurn()
117
+ #Toggle current player
118
+ if @currentturn == 'X'
119
+ @currentturn = 'O'
120
+ else
121
+ @currentturn = 'X'
122
+ end
123
+ end
124
+
125
+ def SpaceAvailable(move)
126
+ if @winner == ''
127
+ return @board[move-1] == '_'
128
+ else
129
+ return false
130
+ end
131
+ end
132
+
133
+ def MakeMove(move)
134
+ #store up to 2 moves for undo
135
+ @penultimatemoveindex = @lastmoveindex
136
+ @lastmoveindex = move-1
137
+ #Array index is one less than move space
138
+ if @board[@lastmoveindex] == '_'
139
+ @board[@lastmoveindex] = @currentturn
140
+
141
+ #Increment move counter
142
+ @movenum += 1
143
+ @movesuccess = true
144
+ else
145
+ #Do not allow player to choose space that has already been played
146
+ @movesuccess = false
147
+ end
148
+ end
149
+
150
+ def ComputerMove()
151
+ if @player2.type == 'computer' and @player2.mark == 'O'
152
+ return ComputerMoveO()
153
+ elsif @player2.type == 'computer' and @player2.mark == 'X'
154
+ return ComputerMoveX()
155
+ end
156
+ end
157
+
158
+ def GetLastMove()
159
+ return @lastmoveindex
160
+ end
161
+
162
+ def GetPenultimateMove()
163
+ return @penultimatemoveindex
164
+ end
165
+
166
+ def UndoMove()
167
+ if @lastmoveindex == -1
168
+ return "Move cannot be undone"
169
+ else
170
+ if @player2.type=='computer'
171
+ if @player2.mark == 'O' or (@player2.mark == 'X' and @movenum > 1)
172
+ #Undo computer and player move
173
+ #Clear 2 moves from board
174
+ @board[@lastmoveindex] = '_'
175
+ @board[@penultimatemoveindex] = '_'
176
+ @lastmoveindex = -1
177
+ @penultimatemoveindex = -1
178
+ @movenum -= 2
179
+ else
180
+ return "Move cannot be undone"
181
+ end
182
+ else
183
+ #Undo player move only
184
+ #Clear move
185
+ @board[@lastmoveindex] = '_'
186
+ @lastmoveindex = -1
187
+ #Decrement move counter
188
+ @movenum -= 1
189
+
190
+ #Toggle current player
191
+ if @currentturn == 'X'
192
+ @currentturn = 'O'
193
+ else
194
+ @currentturn = 'X'
195
+ end
196
+ end
197
+ return "Move undone"
198
+ end
199
+ end
200
+
201
+ def ShowComputerMove(move)
202
+ #Need to increment index to match normal layout
203
+ if @keyboard
204
+ movestring = @keyboardboard[move]
205
+ elsif @numpad
206
+ movestring = @numpadboard[move]
207
+ else
208
+ movestring = (move+1).to_s
209
+ end
210
+ return "Computer chooses " + movestring
211
+ end
212
+
213
+ def CheckWinner(lastmove)
214
+ lastmoveindex = lastmove - 1
215
+ if @movenum < 3
216
+ #Game cannot end in less than 5 moves
217
+ #However, computer uses this to check for blocks on move 4
218
+ return ''
219
+ else
220
+ row = lastmoveindex / 3
221
+ #Determine row to check using integer division
222
+ if (row == 0 and CheckWinTopRow()) or (row == 1 and CheckWinCenterRow()) or (row == 2 and CheckWinBottomRow())
223
+ @winner = @currentturn
224
+ end
225
+
226
+ column = lastmoveindex % 3
227
+ #Determine column to check
228
+ if (column == 0 and CheckWinLeftColumn()) or (column == 1 and CheckWinMiddleColumn()) or (column == 2 and CheckWinRightColumn())
229
+ @winner = @currentturn
230
+ end
231
+
232
+ if lastmoveindex % 2 == 0
233
+ #Determine diagonals to check
234
+ if lastmoveindex % 4 == 2
235
+ if CheckWinBottomLeftToTopRight() then @winner = @currentturn end
236
+ elsif lastmoveindex != 4 and lastmoveindex %4 == 0
237
+ if CheckWinTopLeftToBottomRight() then @winner = @currentturn end
238
+ elsif lastmoveindex == 4
239
+ if CheckWinTopLeftToBottomRight() or CheckWinBottomLeftToTopRight()
240
+ @winner = @currentturn
241
+ end
242
+ end
243
+ end
244
+ end
245
+
246
+ if @movenum == 9 and @winner == ''
247
+ #Game over, no winner; cat's game
248
+ @winner = 'C'
249
+ end
250
+
251
+ return @winner
252
+ end
253
+
254
+
255
+ private
256
+
257
+ def ComputerMoveX()
258
+
259
+ if @difficulty == 'easy'
260
+ #Easy computer moves randomly
261
+ move = RandomMove()
262
+ elsif @difficulty == 'normal'
263
+ #Normal computer moves randomly early on, but looks for wins or blocks as the game progresses
264
+ if @movenum < 3
265
+ move = RandomMove()
266
+ else
267
+ #Check for winning move first
268
+ move = FindWinningMove()
269
+ if move == -1
270
+ #No winning move available, try block next
271
+ move = FindBlockingMove()
272
+ if move == -1
273
+ move = RandomMove()
274
+ end
275
+ end
276
+ end
277
+ elsif @difficulty == 'hard'
278
+ #Hard computer knows what move to make in every situation, until cat game is guaranteed
279
+ case movenum
280
+ when 0
281
+ move = 0
282
+ when 2
283
+ case @lastmoveindex
284
+ when 1,3,5,7
285
+ move = 4
286
+ when 2,4,6
287
+ move = 8
288
+ else
289
+ move = 2
290
+ end
291
+ when 4
292
+ move = FindWinningMove()
293
+ if move == -1
294
+ move = FindBlockingMove()
295
+ if move == -1
296
+ move = 6
297
+ end
298
+ end
299
+ else
300
+ move = FindWinningMove()
301
+ if move == -1
302
+ move = FindBlockingMove()
303
+ if move == -1
304
+ move = RandomMove()
305
+ end
306
+ end
307
+ end
308
+ end
309
+ return move + 1
310
+ end
311
+
312
+ def ComputerMoveO()
313
+ if @winner == ''
314
+ if @difficulty == 'easy'
315
+ #Easy computer moves randomly
316
+ move = RandomMove()
317
+ elsif @difficulty == 'normal'
318
+ #Normal computer moves randomly early on, but looks for wins or blocks as the game progresses
319
+ if @movenum < 3
320
+ move = RandomMove()
321
+ else
322
+ #Check for winning move first
323
+ move = FindWinningMove()
324
+ if move == -1
325
+ #No winning move available, try block next
326
+ move = FindBlockingMove()
327
+ if move == -1 then
328
+ move = RandomMove()
329
+ end
330
+ end
331
+ end
332
+ elsif @difficulty == 'hard'
333
+ #Hard computer knows what move to make in every situation, until cat game is guaranteed
334
+ move = -1
335
+ if @movenum == 1
336
+ if @board[4] == '_'
337
+ move = 4
338
+ else
339
+ move = 0
340
+ end
341
+ elsif @movenum == 3
342
+ if @board[4] == 'X'
343
+ if @board[0] != '_' and @board[8] != '_'
344
+ move = 2
345
+ elsif @board[2] != '_' and @board[6] != '_'
346
+ move = 0
347
+ end
348
+ else
349
+ if (@board[1] == 'X' and @board[5] == 'X') or (@board[3] == 'X' and @board[7] == 'X')
350
+ move = 0
351
+ elsif (@board[3] == 'X' and @board[8] == 'X') or (@board[5] == 'X' and @board[6] == 'X') or (@board[3] == 'X' and @board[5] == 'X') or (@board[0] == 'X' and @board[8] == 'X') or (@board[2] == 'X' and @board[6] == 'X')
352
+ move = 1
353
+ elsif (@board[1] == 'X' and @board[3] == 'X') or (@board[5] == 'X' and @board[7] == 'X')
354
+ move = 2
355
+ elsif (@board[2] == 'X' and @board[7] == 'X') or (@board[1] == 'X' and @board[8] == 'X') or (@board[1] == 'X' and @board[7] == 'X')
356
+ move = 3
357
+ elsif (@board[1] == 'X' and @board[6] == 'X') or (@board[0] == 'X' and @board[7] == 'X')
358
+ move = 5
359
+ elsif (@board[0] == 'X' and @board[5] == 'X') or (@board[2] == 'X' and @board[3] == 'X')
360
+ move = 7
361
+ end
362
+ end
363
+ elsif @movenum == 5
364
+ if (@board[4] == 'O' and @board[5] == 'X' and @board[7] == 'X' and @board[1] != '_' and @board[3] != '_')
365
+ move = 0
366
+ end
367
+ end
368
+ if move == -1
369
+ #Check for winning move first
370
+ move = FindWinningMove()
371
+ if move == -1
372
+ #No winning move available, try block next
373
+ move = FindBlockingMove()
374
+ if move == -1 then
375
+ #puts "Select side"
376
+ if @board[1] == '_'
377
+ move = 1
378
+ elsif @board[3] == '_'
379
+ move = 3
380
+ elsif @board[5] == '_'
381
+ move = 5
382
+ elsif @board[7] == '_'
383
+ move = 7
384
+ end
385
+ end
386
+ end
387
+ end
388
+
389
+
390
+ end
391
+ return (move+1)
392
+ end
393
+ end
394
+
395
+ def FindWinningMove()
396
+ #Pretend O went in any available square and check for win
397
+ for i in 0..8
398
+ if @board[i] == '_'
399
+ @board[i] = @player2.mark
400
+ if CheckWinner(i+1) == @player2.mark
401
+ @board[i] = '_'
402
+ @winner = ''
403
+ return i
404
+ end
405
+ @board[i] = '_'
406
+ end
407
+ end
408
+ return -1
409
+ end
410
+
411
+ def FindBlockingMove()
412
+ #Pretend X went in any available square and check for win; that space necessitates a block
413
+ for i in 0..8
414
+ if @board[i] == '_'
415
+ @board[i] = @player1.mark
416
+ #CheckWinner returns currentturn, so it will still be player2
417
+ if CheckWinner(i+1) == @player2.mark
418
+ @board[i] = '_'
419
+ @winner = ''
420
+ return i
421
+ end
422
+ @board[i] = '_'
423
+ end
424
+ end
425
+ return -1
426
+ end
427
+
428
+ def RandomMove()
429
+ #Select random number 0-8 inclusive; this will match board index
430
+ move = rand(9)
431
+ while @board[move] != '_'
432
+ move = rand(9)
433
+ end
434
+ return move
435
+ end
436
+
437
+ def CheckWinLeftColumn()
438
+ #[0 _ _]
439
+ #[3 _ _]
440
+ #[6 _ _]
441
+ return ((@board[0] == @board[3]) and (@board[0] == @board[6]))
442
+ end
443
+
444
+ def CheckWinMiddleColumn()
445
+ #[_ 1 _]
446
+ #[_ 4 _]
447
+ #[_ 7 _]
448
+ return ((@board[4] == @board[1]) and (@board[4] == @board[7]))
449
+ end
450
+
451
+ def CheckWinRightColumn()
452
+ #[_ _ 2]
453
+ #[_ _ 5]
454
+ #[_ _ 8]
455
+ return ((@board[8] == @board[2]) and (@board[8] == @board[5]))
456
+ end
457
+
458
+ def CheckWinTopRow()
459
+ #[0 1 2]
460
+ #[_ _ _]
461
+ #[_ _ _]
462
+ return ((@board[0] == @board[1]) and (@board[0] == @board[2]))
463
+ end
464
+
465
+ def CheckWinCenterRow()
466
+ #[_ _ _]
467
+ #[3 4 5]
468
+ #[_ _ _]
469
+ return ((@board[4] == @board[3]) and (@board[4] == @board[5]))
470
+ end
471
+
472
+ def CheckWinBottomRow()
473
+ #[_ _ _]
474
+ #[_ _ _]
475
+ #[6 7 8]
476
+ return ((@board[8] == @board[7]) and (@board[8] == @board[6]))
477
+ end
478
+
479
+ def CheckWinTopLeftToBottomRight()
480
+ #[0 _ _]
481
+ #[_ 4 _]
482
+ #[_ _ 8]
483
+ return ((@board[4] == @board[0]) and (@board[4] == @board[8]))
484
+ end
485
+
486
+ def CheckWinBottomLeftToTopRight()
487
+ #[_ _ 2]
488
+ #[_ 4 _]
489
+ #[6 _ _]
490
+ return ((@board[4] == @board[2]) and (@board[4] == @board[6]))
491
+ end
492
+ end
data/bin/x.jpg ADDED
Binary file
data/tictactoe_randall ADDED
@@ -0,0 +1,13 @@
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
+ board = Board.new.show
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tictactoe-randall
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Randall Reed
9
+ autorequire:
10
+ bindir:
11
+ - .
12
+ cert_chain: []
13
+ date: 2013-11-19 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: vrlib
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 0.0.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: 0.0.1
31
+ - !ruby/object:Gem::Dependency
32
+ name: gtk2
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: 0.0.1
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: 0.0.1
47
+ - !ruby/object:Gem::Dependency
48
+ name: require_all
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.0.1
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: 0.0.1
63
+ description:
64
+ email:
65
+ executables:
66
+ - tictactoe_randall
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - bin/Board.rb
71
+ - bin/ModalWindow.rb
72
+ - bin/player.rb
73
+ - bin/tictactoe.rb
74
+ - bin/glade/Board.glade
75
+ - bin/glade/ModalWindow.glade
76
+ - bin/blank.jpg
77
+ - bin/o.jpg
78
+ - bin/x.jpg
79
+ - ./tictactoe_randall
80
+ homepage:
81
+ licenses: []
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - .
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project: nowarning
100
+ rubygems_version: 1.8.24
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: GUI Tic Tac Toe game.
104
+ test_files: []