smagacor 0.0.1 → 0.0.2
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/ChangeLog +62 -0
- data/Rakefile +86 -71
- data/TODO +19 -2
- data/VERSION +1 -1
- data/data/smagacor/smagacor-logo.png +0 -0
- data/data/smagacor/sokoban/crate.png +0 -0
- data/data/smagacor/sokoban/floor.png +0 -0
- data/data/smagacor/sokoban/game.info +7 -0
- data/data/smagacor/sokoban/levels/loma.lvl +288 -0
- data/data/smagacor/sokoban/levels/mas_microban.lvl +1812 -0
- data/data/smagacor/sokoban/levels/mas_sasquatch.lvl +891 -0
- data/data/smagacor/sokoban/levels/microban.lvl +1834 -0
- data/data/smagacor/sokoban/levels/sasquatch.lvl +846 -0
- data/data/smagacor/sokoban/levels/sasquatch_III.lvl +903 -0
- data/data/smagacor/sokoban/levels/sasquatch_IV.lvl +812 -0
- data/data/smagacor/sokoban/levels/sasquatch_V.lvl +868 -0
- data/data/smagacor/sokoban/levels/sasquatch_VI.lvl +902 -0
- data/data/smagacor/sokoban/levels/sasquatch_VII.lvl +898 -0
- data/data/smagacor/sokoban/man.png +0 -0
- data/data/smagacor/sokoban/sokoban.png +0 -0
- data/data/smagacor/sokoban/sokobanui.rb +597 -0
- data/data/smagacor/sokoban/storage.png +0 -0
- data/data/smagacor/sokoban/wall.png +0 -0
- data/data/smagacor/tictactoe/game.info +1 -1
- data/data/smagacor/tictactoe/tictactoe.png +0 -0
- data/data/smagacor/tictactoe/tictactoe.rb +295 -206
- data/data/smagacor/tictactoe/x.png +0 -0
- data/lib/smagacor/listener.rb +50 -0
- data/lib/smagacor/smagacor-ui.rb +208 -127
- data/lib/smagacor/{controller.rb → util.rb} +106 -7
- metadata +68 -45
- data/install.rb +0 -21
Binary file
|
Binary file
|
Binary file
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#
|
2
2
|
#--
|
3
3
|
#
|
4
|
-
# $Id: tictactoe.rb
|
4
|
+
# $Id: tictactoe.rb 362 2005-11-26 17:56:46Z thomas $
|
5
5
|
#
|
6
6
|
# smagacor - a collection of small games in ruby
|
7
7
|
# Copyright (C) 2004 Thomas Leitner
|
@@ -19,280 +19,369 @@
|
|
19
19
|
#
|
20
20
|
#++
|
21
21
|
#
|
22
|
-
require '
|
22
|
+
require 'smagacor/listener'
|
23
23
|
|
24
|
-
|
24
|
+
# TicTacToe for Smagacor
|
25
|
+
module Smagacor::TicTacToe
|
25
26
|
|
26
|
-
#
|
27
|
-
|
27
|
+
# Base player class
|
28
|
+
class Player
|
28
29
|
|
29
|
-
#
|
30
|
-
|
30
|
+
# Return a new player with the +sign+.
|
31
|
+
def initialize
|
32
|
+
@field = -1
|
33
|
+
end
|
31
34
|
|
32
|
-
|
33
|
-
|
35
|
+
# Defines whether the player has selected a field. Default is +false+.
|
36
|
+
def field_selected?
|
37
|
+
@field != -1
|
38
|
+
end
|
34
39
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
40
|
+
# Invoked by the game engine to get the field. The field is reset to the standard value so that
|
41
|
+
# #field_selected? returns false.
|
42
|
+
def selected_field
|
43
|
+
field, @field = @field, -1
|
44
|
+
field
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
39
48
|
|
40
|
-
# Invoked by the game engine to get the move.
|
41
|
-
def move() end
|
42
49
|
|
50
|
+
# Human player. Provides the interface for a human to play TicTacToe.
|
51
|
+
class HumanPlayer < Player
|
52
|
+
|
53
|
+
# Set the +field+ which is returned by the next #selcted_field.
|
54
|
+
def set_field( field )
|
55
|
+
@field = field
|
43
56
|
end
|
44
57
|
|
58
|
+
end
|
45
59
|
|
46
|
-
# Human player. Provides the interface for a human to play TicTacToe.
|
47
|
-
class HumanPlayer < Player
|
48
60
|
|
49
|
-
|
50
|
-
|
51
|
-
@field = -1
|
52
|
-
end
|
61
|
+
# AI computer player. Not yet implemented!
|
62
|
+
class ComputerPlayer < Player
|
53
63
|
|
54
|
-
|
55
|
-
|
56
|
-
self.can_move = false
|
57
|
-
@field
|
58
|
-
end
|
64
|
+
def selected_field
|
65
|
+
end
|
59
66
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# The TicTacToe board.
|
71
|
+
class Board < String
|
72
|
+
|
73
|
+
# Defines the empty field.
|
74
|
+
FIELD_EMPTY = ?-
|
75
|
+
|
76
|
+
def initialize( size )
|
77
|
+
super( FIELD_EMPTY.chr * size )
|
78
|
+
end
|
66
79
|
|
80
|
+
# Redefined each so that the methods from Enumerable work correctly.
|
81
|
+
def each( &block )
|
82
|
+
each_byte( &block )
|
67
83
|
end
|
68
84
|
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
# Game engine for TicTacToe. Knows how to correctly play TicTacToe.
|
89
|
+
class TicTacToe
|
90
|
+
|
91
|
+
PLAYER1 = ?1
|
92
|
+
PLAYER2 = ?2
|
93
|
+
|
94
|
+
include Listener
|
69
95
|
|
70
|
-
#
|
71
|
-
|
96
|
+
# The board on which is played.
|
97
|
+
attr_accessor :board
|
72
98
|
|
73
|
-
|
99
|
+
# The current player.
|
100
|
+
attr_reader :cur_player
|
101
|
+
|
102
|
+
# Create a new TicTacToe game with the given +players+.
|
103
|
+
def initialize( player1, player2 )
|
104
|
+
@player1 = player1
|
105
|
+
@player2 = player2
|
106
|
+
@board = Board.new( 9 )
|
107
|
+
add_msg_name( :move )
|
108
|
+
add_msg_name( :finished )
|
109
|
+
end
|
110
|
+
|
111
|
+
# Initialize the game state.
|
112
|
+
def init
|
113
|
+
@board = Board.new( 9 )
|
114
|
+
@cur_player = @player1
|
115
|
+
end
|
116
|
+
|
117
|
+
# Play one (or more) round(s) of a TicTacToe game. When the current player has made his move
|
118
|
+
# (Player#field_selected?), his move is verified and the board changed. Then it is the turn of the
|
119
|
+
# other player. As long as the current player can make a move (and the game is not finished),
|
120
|
+
# the method runs. When the current player cannot make a move, the method exists and it has to
|
121
|
+
# be called again, when the current player can make his move.
|
122
|
+
def play_round
|
123
|
+
while @cur_player.field_selected? && !game_finished?
|
124
|
+
logger.info { "Current board: #{@board}" }
|
125
|
+
logger.info { "Current player: #{sign(@cur_player).chr}" }
|
126
|
+
field = @cur_player.selected_field
|
127
|
+
logger.info { "Field selected: #{field}" }
|
128
|
+
if @board[field] == Board::FIELD_EMPTY
|
129
|
+
@board[field] = sign( @cur_player )
|
130
|
+
switch_players
|
131
|
+
dispatch_msg( :move, field, @board[field] )
|
132
|
+
end
|
74
133
|
end
|
134
|
+
if game_finished?
|
135
|
+
logger.info { "Game finished" }
|
136
|
+
dispatch_msg( :finished, player_won )
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
#######
|
141
|
+
private
|
142
|
+
#######
|
75
143
|
|
144
|
+
def sign( player )
|
145
|
+
( player == @player1 ? PLAYER1 : PLAYER2 )
|
76
146
|
end
|
77
147
|
|
148
|
+
def switch_players
|
149
|
+
@cur_player = ( @cur_player == @player1 ? @player2 : @player1 )
|
150
|
+
end
|
78
151
|
|
79
|
-
#
|
80
|
-
|
152
|
+
# True if the board is full.
|
153
|
+
def board_full?
|
154
|
+
@board.all? {|i| i != Board::FIELD_EMPTY}
|
155
|
+
end
|
81
156
|
|
82
|
-
|
83
|
-
|
157
|
+
# True if the game is over
|
158
|
+
def game_finished?
|
159
|
+
board_full? || player_won?( PLAYER1 ) || player_won?( PLAYER2 )
|
160
|
+
end
|
84
161
|
|
85
|
-
|
86
|
-
|
87
|
-
|
162
|
+
# Return the number of the player who has won.
|
163
|
+
#
|
164
|
+
# Returns
|
165
|
+
# 0:: player 1
|
166
|
+
# 1:: player 2
|
167
|
+
# 2:: draw
|
168
|
+
# -1:: game not finished yet
|
169
|
+
def player_won
|
170
|
+
( player_won?( PLAYER1 ) ? 0 : ( player_won?( PLAYER2 ) ? 1 : ( board_full? ? 2 : -1 ) ) )
|
171
|
+
end
|
88
172
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
end
|
173
|
+
# Win situations.
|
174
|
+
WIN_SITUATIONS = [[0,1,2], [3,4,5], [6,7,8], [0,3,6],
|
175
|
+
[1,4,7], [2,5,8], [0,4,8], [2,4,6]]
|
93
176
|
|
177
|
+
# Check if +player+ has won the game.
|
178
|
+
def player_won?( player )
|
179
|
+
WIN_SITUATIONS.each {|a,b,c| return true if @board[a] == player && @board[b] == player && @board[c] == player }
|
180
|
+
return false
|
94
181
|
end
|
95
182
|
|
183
|
+
end
|
96
184
|
|
97
|
-
# Game engine for TicTacToe. Knows how to correctly play TicTacToe.
|
98
|
-
class TicTacToe
|
99
185
|
|
100
|
-
|
186
|
+
class TicTacToeCanvas < Qt::Widget
|
101
187
|
|
102
|
-
|
103
|
-
attr_accessor :board
|
188
|
+
signals 'clicked(int)'
|
104
189
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
190
|
+
def initialize( *args )
|
191
|
+
super( *args )
|
192
|
+
setBackgroundMode( Qt::NoBackground )
|
193
|
+
@x = @sx = Qt::Image.new( File.join( File.dirname( __FILE__ ), 'x.png' ) )
|
194
|
+
@o = @so = Qt::Image.new( File.join( File.dirname( __FILE__ ), 'o.png' ) )
|
195
|
+
end
|
110
196
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
197
|
+
def game=( game )
|
198
|
+
@game = game
|
199
|
+
@game.add_msg_listener( :move, method(:onMove) )
|
200
|
+
end
|
115
201
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
202
|
+
def onMove( field, player )
|
203
|
+
painter = Qt::Painter.new( self )
|
204
|
+
case player
|
205
|
+
when TicTacToe::PLAYER1 then paintX( painter, field )
|
206
|
+
when TicTacToe::PLAYER2 then paintO( painter, field )
|
120
207
|
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def paintPlayArea( painter )
|
211
|
+
painter.setPen( Qt::Pen.new( Qt::black, @b_padding/10, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) )
|
212
|
+
painter.drawLine( @b_padding + @b_width/3, @b_padding, @b_padding + @b_width/3, @b_padding + @b_width )
|
213
|
+
painter.drawLine( @b_padding + @b_width*2/3, @b_padding, @b_padding + @b_width*2/3, @b_padding + @b_width )
|
214
|
+
painter.drawLine( @b_padding, @b_padding + @b_width/3, @b_padding + @b_width, @b_padding + @b_width/3 )
|
215
|
+
painter.drawLine( @b_padding, @b_padding + @b_width*2/3, @b_padding + @b_width, @b_padding + @b_width*2/3 )
|
216
|
+
end
|
121
217
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
while @players[@cur_player].can_move && !game_finished?
|
129
|
-
logger.info { "Current board: #{@board}" }
|
130
|
-
logger.info { "Current player: #{cur_player}" }
|
131
|
-
field = @players[@cur_player].move
|
132
|
-
logger.info { "Field selected: #{field}" }
|
133
|
-
if @board[field] == Board::FIELD_EMPTY
|
134
|
-
@board[field] = @cur_player.to_s
|
135
|
-
@cur_player = 1 - @cur_player
|
136
|
-
changed
|
137
|
-
notify_observers( :move )
|
218
|
+
def paintCurGame( painter )
|
219
|
+
unless @game.nil?
|
220
|
+
@game.board.each_with_index do |item, index|
|
221
|
+
case item
|
222
|
+
when TicTacToe::PLAYER1 then paintX( painter, index )
|
223
|
+
when TicTacToe::PLAYER2 then paintO( painter, index )
|
138
224
|
end
|
139
225
|
end
|
140
|
-
if game_finished?
|
141
|
-
logger.info { " Game finished" }
|
142
|
-
changed
|
143
|
-
notify_observers( :finished, player_won )
|
144
|
-
end
|
145
226
|
end
|
227
|
+
end
|
146
228
|
|
147
|
-
|
229
|
+
def paintX( painter, field )
|
230
|
+
x, y, w = field_info( field )
|
231
|
+
@sx = @x.smoothScale( w, w ) if @sx.width != w
|
232
|
+
painter.drawImage( x, y, @sx )
|
233
|
+
end
|
148
234
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
235
|
+
def paintO( painter, field )
|
236
|
+
x, y, w = field_info( field )
|
237
|
+
@so = @o.smoothScale( w, w ) if @so.width != w
|
238
|
+
painter.drawImage( x, y, @so )
|
239
|
+
end
|
153
240
|
|
154
|
-
|
155
|
-
|
156
|
-
|
241
|
+
def paintEvent( event )
|
242
|
+
pix = Qt::Pixmap.new( width, height )
|
243
|
+
painter = Qt::Painter.new
|
244
|
+
painter.begin( pix )
|
245
|
+
painter.setBrush( Qt::white )
|
246
|
+
painter.drawRect( 0, 0, self.width, self.height )
|
247
|
+
paintPlayArea( painter )
|
248
|
+
paintCurGame( painter )
|
249
|
+
painter.end
|
250
|
+
|
251
|
+
p = Qt::Painter.new( self )
|
252
|
+
p.drawPixmap( 0, 0, pix )
|
253
|
+
end
|
157
254
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
255
|
+
def resizeEvent( event )
|
256
|
+
width = (self.width > self.height ? self.height : self.width )
|
257
|
+
@b_width = (width * 0.8).to_i
|
258
|
+
@b_padding = (width * 0.1).to_i
|
259
|
+
@f_width = @b_width/5
|
260
|
+
@f_padding = @b_padding + (@b_width/3 - @f_width)/2
|
261
|
+
end
|
162
262
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
# 0:: player 1
|
167
|
-
# 1:: player 2
|
168
|
-
# 2:: draw
|
169
|
-
# -1:: game not finished yet
|
170
|
-
def player_won
|
171
|
-
( player_won?( ?0 ) ? 0 : ( player_won?( ?1 ) ? 1 : ( board_full? ? 2 : -1 ) ) )
|
172
|
-
end
|
263
|
+
def field_info( field )
|
264
|
+
[@f_padding + @b_width/3 * (field % 3), @f_padding + @b_width/3 * (field / 3), @f_width]
|
265
|
+
end
|
173
266
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
267
|
+
def mouseReleaseEvent( event )
|
268
|
+
pos = Proc.new do |point|
|
269
|
+
case point
|
270
|
+
when 0..(@b_padding + @b_width/3): 0
|
271
|
+
when (@b_padding + @b_width/3)..(@b_padding + @b_width*2/3): 1
|
272
|
+
else 2
|
273
|
+
end
|
178
274
|
end
|
179
|
-
|
275
|
+
emit clicked( pos[event.x] + pos[event.y]*3 )
|
180
276
|
end
|
181
277
|
|
278
|
+
end
|
182
279
|
|
183
|
-
# Widget for TicTacToe.
|
184
|
-
class TicTacToeUI < FXHorizontalFrame
|
185
280
|
|
186
|
-
|
281
|
+
# Widget for TicTacToe.
|
282
|
+
class TicTacToeUI < Qt::Widget
|
187
283
|
|
188
|
-
|
189
|
-
super( p )
|
190
|
-
@gameinfo = gameinfo
|
284
|
+
slots 'start_game()', 'clicked(int)'
|
191
285
|
|
192
|
-
|
193
|
-
@canvas.connect( SEL_PAINT, method( :onPaint ) )
|
194
|
-
@canvas.connect( SEL_LEFTBUTTONRELEASE, method( :onCanvasClick ) )
|
195
|
-
FXVerticalSeparator.new( self )
|
196
|
-
options = FXVerticalFrame.new( self, LAYOUT_FILL_Y )
|
286
|
+
attr_reader :gameinfo
|
197
287
|
|
198
|
-
|
199
|
-
FXLabel.new( options, name )
|
200
|
-
box = FXComboBox.new( options, 20, 2, nil, 0, COMBOBOX_NORMAL|COMBOBOX_STATIC|FRAME_SUNKEN )
|
201
|
-
box.appendItem( 'Human', HumanPlayer )
|
202
|
-
box.appendItem( 'Computer', ComputerPlayer )
|
203
|
-
box
|
204
|
-
end
|
205
|
-
@player1 = p.call( 'Player 1' )
|
206
|
-
@player2 = p.call( 'Player 2' )
|
288
|
+
PLAYERS = {'Human' => HumanPlayer, 'Computer' => ComputerPlayer}
|
207
289
|
|
208
|
-
|
290
|
+
def initialize( p, gameinfo )
|
291
|
+
super( p )
|
292
|
+
@gameinfo = gameinfo
|
209
293
|
|
210
|
-
|
211
|
-
|
212
|
-
end
|
294
|
+
@canvas = TicTacToeCanvas.new( self )
|
295
|
+
connect( @canvas, SIGNAL('clicked(int)'), self, SLOT('clicked(int)') )
|
213
296
|
|
214
|
-
|
215
|
-
|
297
|
+
optionsLayout = Qt::VBoxLayout.new
|
298
|
+
optionsLayout.setSpacing( 3 )
|
299
|
+
optionsLayout.setMargin( 6 )
|
300
|
+
p = Proc.new do |name|
|
301
|
+
optionsLayout.addWidget( Qt::Label.new( name, self ) )
|
302
|
+
box = Qt::ComboBox.new( false, self )
|
303
|
+
optionsLayout.addWidget( box )
|
304
|
+
box.insertItem( 'Human' )
|
305
|
+
box.insertItem( 'Computer' )
|
306
|
+
box
|
216
307
|
end
|
308
|
+
@player1 = p.call( 'Player 1' )
|
309
|
+
@player2 = p.call( 'Player 2' )
|
217
310
|
|
218
|
-
|
219
|
-
|
220
|
-
width, padding = get_lengths
|
221
|
-
pos = Proc.new do |point|
|
222
|
-
case point
|
223
|
-
when 0..(padding + width/3): 0
|
224
|
-
when (padding + width/3)..(padding + width*2/3): 1
|
225
|
-
else 2
|
226
|
-
end
|
227
|
-
end
|
228
|
-
@game.cur_player.set_field( pos[event.click_x] + pos[event.click_y]*3 )
|
229
|
-
@game.play_round
|
230
|
-
end
|
231
|
-
end
|
311
|
+
button = Qt::PushButton.new( "Start Game", self )
|
312
|
+
connect( button, SIGNAL('clicked()'), self, SLOT('start_game()') )
|
232
313
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
314
|
+
optionsLayout.addSpacing( 10 )
|
315
|
+
optionsLayout.addWidget( button )
|
316
|
+
optionsLayout.addStretch
|
317
|
+
|
318
|
+
@xicon = File.join( gameinfo.directory, 'x.png' )
|
319
|
+
@oicon = File.join( gameinfo.directory, 'o.png' )
|
320
|
+
|
321
|
+
layout = Qt::HBoxLayout.new( self )
|
322
|
+
layout.addWidget( @canvas, 1 )
|
323
|
+
layout.addLayout( optionsLayout )
|
324
|
+
end
|
325
|
+
|
326
|
+
def clicked( field )
|
327
|
+
if !@game.nil? && @game.cur_player.respond_to?( :set_field )
|
328
|
+
@game.cur_player.set_field( field )
|
239
329
|
@game.play_round
|
240
330
|
end
|
331
|
+
end
|
241
332
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
333
|
+
def start_game
|
334
|
+
@game = TicTacToe.new( PLAYERS[@player1.currentText].new, PLAYERS[@player2.currentText].new )
|
335
|
+
@canvas.game = @game
|
336
|
+
@game.add_msg_listener( :move, method(:onMove) )
|
337
|
+
@game.add_msg_listener( :finished, method(:finished) )
|
338
|
+
@game.init
|
339
|
+
@game.play_round
|
340
|
+
@canvas.update
|
341
|
+
end
|
250
342
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
dc.fillRectangle( 0, 0, sender.width, sender.height )
|
255
|
-
paintPlayArea( dc )
|
256
|
-
paintCurGame( dc )
|
257
|
-
end
|
258
|
-
end
|
343
|
+
def onMove( field, player )
|
344
|
+
@manager.add_undo_info( [field, player] ) # TODO think about AI moves, player changes etc
|
345
|
+
end
|
259
346
|
|
260
|
-
|
347
|
+
def finished( winner )
|
348
|
+
msg = (winner < 2 ? "Player #{winner+1}" : "Nobody" ) + " has won!"
|
349
|
+
Qt::MessageBox.information( self, "Result", msg, Qt::MessageBox::Ok )
|
350
|
+
end
|
261
351
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
352
|
+
def set_undo_manager( manager )
|
353
|
+
@manager = manager
|
354
|
+
manager.add_msg_listener( :undo, method(:undo) )
|
355
|
+
manager.add_msg_listener( :redo, method(:redo) )
|
356
|
+
end
|
266
357
|
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
dc.drawLine( padding + width/3, padding, padding + width/3, padding + width )
|
273
|
-
dc.drawLine( padding + width*2/3, padding, padding + width*2/3, padding + width )
|
274
|
-
dc.drawLine( padding, padding + width/3, padding + width, padding + width/3 )
|
275
|
-
dc.drawLine( padding, padding + width*2/3, padding + width, padding + width*2/3 )
|
276
|
-
end
|
358
|
+
def undo( info )
|
359
|
+
#TODO also undo cur player
|
360
|
+
@game.board[info[0]] = Board::FIELD_EMPTY
|
361
|
+
@canvas.update
|
362
|
+
end
|
277
363
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
364
|
+
def redo( info )
|
365
|
+
@game.board[info[0]] = info[1]
|
366
|
+
@canvas.update
|
367
|
+
end
|
368
|
+
|
369
|
+
end
|
370
|
+
|
371
|
+
|
372
|
+
class TicTacToeGame < Smagacor::GamePlugin
|
373
|
+
|
374
|
+
def initialize( p, gameinfo )
|
375
|
+
@parent = p
|
376
|
+
@widget = TicTacToeUI.new( p, gameinfo )
|
377
|
+
end
|
378
|
+
|
379
|
+
def game_widget
|
380
|
+
@widget
|
381
|
+
end
|
295
382
|
|
383
|
+
def set_undo_manager( manager )
|
384
|
+
@widget.set_undo_manager( manager )
|
296
385
|
end
|
297
386
|
|
298
387
|
end
|