green_shoes 0.198.0 → 0.201.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 +1 -1
- data/VERSION +1 -1
- data/lib/shoes/app.rb +1 -1
- data/lib/shoes/widget.rb +3 -3
- data/samples/sample50.rb +326 -0
- data/snapshots/sample50.png +0 -0
- data/static/man-editor-notepad.png +0 -0
- data/static/manual-en.txt +2 -2
- metadata +5 -3
data/README.md
CHANGED
|
@@ -8,7 +8,7 @@ This is **Green Shoes** written in Ruby. One of the colorful [Shoes](http://shoe
|
|
|
8
8
|
|
|
9
9
|
Fantastic logo icon was created by [Zachary Scott](https://github.com/zacharyscott). Thanks!
|
|
10
10
|
|
|
11
|
-
Now, there are
|
|
11
|
+
Now, there are 50 [samples](https://github.com/ashbb/green_shoes/tree/master/samples). Each has a [snapshot](https://github.com/ashbb/green_shoes/tree/master/snapshots).
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
Tiny Streaming Flash videos
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.201.0
|
data/lib/shoes/app.rb
CHANGED
|
@@ -699,7 +699,7 @@ class Shoes
|
|
|
699
699
|
Shoes.call_back_procs self
|
|
700
700
|
end
|
|
701
701
|
|
|
702
|
-
[:append, :prepend, :after, :before].each do |m|
|
|
702
|
+
[:append, :prepend, :after, :before, :click, :hover, :leave, :release].each do |m|
|
|
703
703
|
define_method m do |*args, &blk|
|
|
704
704
|
top_slot.send m, *args, &blk
|
|
705
705
|
end
|
data/lib/shoes/widget.rb
CHANGED
|
@@ -4,13 +4,13 @@ class Shoes
|
|
|
4
4
|
m = klass.inspect.downcase.split('::').last
|
|
5
5
|
Shoes::App.class_eval do
|
|
6
6
|
define_method m do |*args, &blk|
|
|
7
|
-
klass.class_variable_set :@@
|
|
7
|
+
klass.class_variable_set :@@__app__, self
|
|
8
8
|
klass.new *args, &blk
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
11
|
klass.class_eval do
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
define_method :method_missing do |*args, &blk|
|
|
13
|
+
klass.class_variable_get(:@@__app__).send *args, &blk
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
end
|
data/samples/sample50.rb
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
require '../lib/green_shoes'
|
|
2
|
+
# The original is Red Shoes built-in sample: expert-othello.rb
|
|
3
|
+
# A little bit edited for Green Shoes.
|
|
4
|
+
|
|
5
|
+
# Othello
|
|
6
|
+
# by tieg
|
|
7
|
+
# 1/13/08
|
|
8
|
+
# with help: DeeJay, Ryan M. from mailing list
|
|
9
|
+
#
|
|
10
|
+
# FIXME bug if it memorizes it but it's not a valid move
|
|
11
|
+
#
|
|
12
|
+
module Othello
|
|
13
|
+
|
|
14
|
+
PIECE_WIDTH = 62
|
|
15
|
+
PIECE_HEIGHT = 62
|
|
16
|
+
TOP_OFFSET = 47
|
|
17
|
+
LEFT_OFFSET = 12
|
|
18
|
+
SPACE = ' ' * 57
|
|
19
|
+
|
|
20
|
+
class Game
|
|
21
|
+
BOARD_SIZE = [8,8]
|
|
22
|
+
|
|
23
|
+
attr_accessor :p1, :p2, :board, :board_history
|
|
24
|
+
|
|
25
|
+
def initialize
|
|
26
|
+
@board_history = []
|
|
27
|
+
@p1 = Player.new(:black, pieces_per_player)
|
|
28
|
+
@p2 = Player.new(:white, pieces_per_player)
|
|
29
|
+
@board = new_board
|
|
30
|
+
lay_initial_pieces
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def next_turn(check_available_moves=true)
|
|
34
|
+
@current_player = next_player
|
|
35
|
+
if check_available_moves && skip_turn?
|
|
36
|
+
# FIXME Possible infinite loop if neither player has a good move?
|
|
37
|
+
next_turn
|
|
38
|
+
raise "Player #{@current_player.piece} (#{@current_player.color}) has no available moves. Player #{next_player.piece}'s (#{next_player.color}) turn."
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def current_player
|
|
43
|
+
@current_player ||= @p1
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def next_player
|
|
47
|
+
current_player == @p1 ? @p2 : @p1
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Build the array for the board, with zero-based arrays.
|
|
51
|
+
def new_board
|
|
52
|
+
Array.new(BOARD_SIZE[0]) do # build each cols L to R
|
|
53
|
+
Array.new(BOARD_SIZE[1]) do # insert cells in each col
|
|
54
|
+
0
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Lay the initial 4 pieces in the middle
|
|
60
|
+
def lay_initial_pieces
|
|
61
|
+
lay_piece([4, 3], false)
|
|
62
|
+
next_turn(false)
|
|
63
|
+
lay_piece([3, 3], false)
|
|
64
|
+
next_turn(false)
|
|
65
|
+
lay_piece([3, 4], false)
|
|
66
|
+
next_turn(false)
|
|
67
|
+
lay_piece([4, 4], false)
|
|
68
|
+
next_turn(false)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def lay_piece(c=[0,0], check_adjacent_pieces=true)
|
|
72
|
+
memorize_board
|
|
73
|
+
piece = current_player.piece
|
|
74
|
+
opp_piece = current_player.opp_piece
|
|
75
|
+
raise "Spot already taken." if board_at(c) != 0
|
|
76
|
+
if check_adjacent_pieces
|
|
77
|
+
pieces_to_change = []
|
|
78
|
+
pieces_to_change << check_direction(c, [ 0, 1], piece, opp_piece) # N
|
|
79
|
+
pieces_to_change << check_direction(c, [ 1, 1], piece, opp_piece) # NE
|
|
80
|
+
pieces_to_change << check_direction(c, [ 1, 0], piece, opp_piece) # E
|
|
81
|
+
pieces_to_change << check_direction(c, [ 1,-1], piece, opp_piece) # SE
|
|
82
|
+
pieces_to_change << check_direction(c, [ 0,-1], piece, opp_piece) # S
|
|
83
|
+
pieces_to_change << check_direction(c, [-1,-1], piece, opp_piece) # SW
|
|
84
|
+
pieces_to_change << check_direction(c, [-1, 0], piece, opp_piece) # W
|
|
85
|
+
pieces_to_change << check_direction(c, [-1, 1], piece, opp_piece) # NW
|
|
86
|
+
raise "You must move to a spot that will turn your opponent's piece." if pieces_to_change.compact.all? { |a| a.empty? }
|
|
87
|
+
pieces_to_change.compact.each { |direction| direction.each { |i| @board[i[0]][i[1]] = piece } }
|
|
88
|
+
end
|
|
89
|
+
current_player.pieces -= 1
|
|
90
|
+
@board[c[0]][c[1]] = piece
|
|
91
|
+
current_winner = calculate_current_winner
|
|
92
|
+
raise "Game over. Player #{current_winner.piece} wins with #{current_winner.pieces_on_board} pieces!" if @p1.pieces + @p2.pieces == 0
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def skip_turn?
|
|
96
|
+
possibles = []
|
|
97
|
+
@board.each_with_index { |col,col_index|
|
|
98
|
+
col.each_with_index { |cell,row_index|
|
|
99
|
+
return false if possible_move?([col_index,row_index])
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
true
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def possible_move?(c=[0,0])
|
|
106
|
+
return nil if board_at(c) != 0
|
|
107
|
+
possible_moves = []
|
|
108
|
+
piece = current_player.piece
|
|
109
|
+
opp_piece = current_player.opp_piece
|
|
110
|
+
pieces_to_change = []
|
|
111
|
+
pieces_to_change << check_direction(c, [ 0, 1], piece, opp_piece) # N
|
|
112
|
+
pieces_to_change << check_direction(c, [ 1, 1], piece, opp_piece) # NE
|
|
113
|
+
pieces_to_change << check_direction(c, [ 1, 0], piece, opp_piece) # E
|
|
114
|
+
pieces_to_change << check_direction(c, [ 1,-1], piece, opp_piece) # SE
|
|
115
|
+
pieces_to_change << check_direction(c, [ 0,-1], piece, opp_piece) # S
|
|
116
|
+
pieces_to_change << check_direction(c, [-1,-1], piece, opp_piece) # SW
|
|
117
|
+
pieces_to_change << check_direction(c, [-1, 0], piece, opp_piece) # W
|
|
118
|
+
pieces_to_change << check_direction(c, [-1, 1], piece, opp_piece) # NW
|
|
119
|
+
return nil if pieces_to_change.compact.all? { |a| a.empty? }
|
|
120
|
+
true
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def memorize_board
|
|
124
|
+
dup_board = new_board
|
|
125
|
+
dup_board = []
|
|
126
|
+
@board.each do |col|
|
|
127
|
+
dup_board << col.dup
|
|
128
|
+
end
|
|
129
|
+
@board_history << { :player => current_player, :board => dup_board }
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def undo!
|
|
133
|
+
last_move = @board_history.pop
|
|
134
|
+
@board = last_move[:board]
|
|
135
|
+
@current_player = last_move[:player]
|
|
136
|
+
@current_player.pieces += 1
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def calculate_current_winner
|
|
140
|
+
@p1.pieces_on_board, @p2.pieces_on_board = 0, 0
|
|
141
|
+
@board.each { |row|
|
|
142
|
+
row.each { |cell|
|
|
143
|
+
if cell == 1
|
|
144
|
+
@p1.pieces_on_board += 1
|
|
145
|
+
else
|
|
146
|
+
@p2.pieces_on_board += 1
|
|
147
|
+
end
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
@p1.pieces_on_board > @p2.pieces_on_board ? @p1 : @p2
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def check_direction(c, dir, piece, opp_piece)
|
|
154
|
+
c_adjacent = next_in_direction(c.dup, dir)
|
|
155
|
+
c_last = nil
|
|
156
|
+
pieces_in_between = []
|
|
157
|
+
# Find the last piece if there is one.
|
|
158
|
+
while(valid_location?(c_adjacent))
|
|
159
|
+
if board_at(c_adjacent) == opp_piece
|
|
160
|
+
pieces_in_between << c_adjacent.dup
|
|
161
|
+
elsif board_at(c_adjacent) == piece && pieces_in_between.size > 0
|
|
162
|
+
c_last = c_adjacent
|
|
163
|
+
break
|
|
164
|
+
else
|
|
165
|
+
break
|
|
166
|
+
end
|
|
167
|
+
c_adjacent = next_in_direction(c_adjacent, dir)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
pieces_in_between.empty? || c_last.nil? ? nil : pieces_in_between
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Find the value of the board at the given coordinate.
|
|
174
|
+
def board_at(c)
|
|
175
|
+
@board[c[0]][c[1]]
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Is this a valid location on board?
|
|
179
|
+
def valid_location?(c=[1,1])
|
|
180
|
+
c[0] >= 0 && c[1] >= 0 && c[0] < BOARD_SIZE[0] && c[1] < BOARD_SIZE[1]
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Perform the operations to get the next spot in the appropriate direction
|
|
184
|
+
def next_in_direction(c, dir)
|
|
185
|
+
c[0] += dir[0]
|
|
186
|
+
c[1] += dir[1]
|
|
187
|
+
c
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
private
|
|
191
|
+
def pieces_per_player
|
|
192
|
+
total_squares / 2
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# The total number of squares
|
|
196
|
+
def total_squares
|
|
197
|
+
BOARD_SIZE[0] * BOARD_SIZE[1]
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
class Player
|
|
201
|
+
attr_accessor :pieces, :color, :pieces_on_board
|
|
202
|
+
|
|
203
|
+
def initialize(color=:black,pieces=0)
|
|
204
|
+
@pieces = pieces
|
|
205
|
+
@pieces_on_board = 0 # used only in calculating winner
|
|
206
|
+
@color = color
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def piece
|
|
210
|
+
color == :black ? 1 : 2
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def opp_piece
|
|
214
|
+
color == :black ? 2 : 1
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def draw_player_1(first_turn=false)
|
|
220
|
+
stack width: width-5, height: 50 do
|
|
221
|
+
if GAME.current_player==GAME.p1
|
|
222
|
+
background yellow
|
|
223
|
+
border black, strokewidth: 10
|
|
224
|
+
para strong("Player 1 (#{GAME.current_player.color}) turn"), margin: [20, 15, 0, 0]
|
|
225
|
+
else
|
|
226
|
+
background white
|
|
227
|
+
border black, strokewidth: 10
|
|
228
|
+
undo = GAME.board_history.empty? ? nil : link("Undo last move"){ GAME.undo!; draw_board }
|
|
229
|
+
para strong("Player 1"), SPACE, undo, margin: [20, 15, 0, 0]
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def draw_player_2(first_turn=false)
|
|
235
|
+
stack width: width-5, height: 50 do
|
|
236
|
+
if GAME.current_player==GAME.p2
|
|
237
|
+
background yellow
|
|
238
|
+
border black, strokewidth: 10
|
|
239
|
+
para strong("Player 2 (#{GAME.current_player.color}) turn"), margin: [20, 15, 0, 0]
|
|
240
|
+
else
|
|
241
|
+
background white
|
|
242
|
+
border black, strokewidth: 10
|
|
243
|
+
undo = GAME.board_history.empty? ? nil : link("Undo last move"){ GAME.undo!; draw_board }
|
|
244
|
+
para strong("Player 2"), SPACE, undo, margin: [20, 15, 0, 0]
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def draw_board
|
|
251
|
+
clear do
|
|
252
|
+
background black
|
|
253
|
+
draw_player_1
|
|
254
|
+
|
|
255
|
+
stack width: width, height: 500 do
|
|
256
|
+
fill rgb(0, 190, 0)
|
|
257
|
+
#rect :left => 0, :top => 0, :width => 495, :height => 495
|
|
258
|
+
|
|
259
|
+
GAME.board.each_with_index do |col, col_index|
|
|
260
|
+
col.each_with_index do |cell, row_index|
|
|
261
|
+
left, top = left_top_corner_of_piece(col_index, row_index)
|
|
262
|
+
left = left - LEFT_OFFSET + 10
|
|
263
|
+
top = top - TOP_OFFSET + 50
|
|
264
|
+
fill rgb(0, 440, 0, 90)
|
|
265
|
+
strokewidth 1
|
|
266
|
+
stroke rgb(0, 100, 0)
|
|
267
|
+
rect :left => left, :top => top, :width => PIECE_WIDTH, :height => PIECE_HEIGHT
|
|
268
|
+
|
|
269
|
+
if cell != 0
|
|
270
|
+
strokewidth 0
|
|
271
|
+
fill (cell == 1 ? rgb(100,100,100) : rgb(155,155,155))
|
|
272
|
+
oval(left+3, top+4, PIECE_WIDTH-10, PIECE_HEIGHT-10)
|
|
273
|
+
|
|
274
|
+
fill (cell == 1 ? black : white)
|
|
275
|
+
oval(left+5, top+5, PIECE_WIDTH-10, PIECE_HEIGHT-10)
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
draw_player_2
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def left_top_corner_of_piece(a,b)
|
|
286
|
+
[(a*PIECE_WIDTH+LEFT_OFFSET), (b*PIECE_HEIGHT+TOP_OFFSET)]
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def right_bottom_corner_of_piece(a,b)
|
|
290
|
+
left_top_corner_of_piece(a,b).map { |coord| coord + PIECE_WIDTH }
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def find_piece(x,y)
|
|
294
|
+
GAME.board.each_with_index { |row_array, row|
|
|
295
|
+
row_array.each_with_index { |col_array, col|
|
|
296
|
+
left, top = left_top_corner_of_piece(col, row).map { |i| i - 5}
|
|
297
|
+
right, bottom = right_bottom_corner_of_piece(col, row).map { |i| i -5 }
|
|
298
|
+
return [col, row] if x >= left && x <= right && y >= top && y <= bottom
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return false
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
Shoes.app :width => 520, :height => 600 do
|
|
307
|
+
extend Othello
|
|
308
|
+
GAME = Othello::Game.new
|
|
309
|
+
|
|
310
|
+
draw_board
|
|
311
|
+
|
|
312
|
+
click { |button, x, y|
|
|
313
|
+
if coords = find_piece(x,y)
|
|
314
|
+
begin
|
|
315
|
+
GAME.lay_piece(coords)
|
|
316
|
+
GAME.next_turn
|
|
317
|
+
draw_board
|
|
318
|
+
rescue => e
|
|
319
|
+
draw_board
|
|
320
|
+
alert(e.message)
|
|
321
|
+
end
|
|
322
|
+
else
|
|
323
|
+
# alert("Not a piece.")
|
|
324
|
+
end
|
|
325
|
+
}
|
|
326
|
+
end
|
|
Binary file
|
|
Binary file
|
data/static/manual-en.txt
CHANGED
|
@@ -77,7 +77,7 @@ We'll talk through all the steps.
|
|
|
77
77
|
|
|
78
78
|
At first, install Ruby. On '''Windows''',
|
|
79
79
|
visit the site of [[http://rubyinstaller.org/ RubyInstaller for Windows]] to
|
|
80
|
-
download '''RubyInstaller 1.9.2
|
|
80
|
+
download the latest '''RubyInstaller 1.9.2'''.
|
|
81
81
|
|
|
82
82
|
Then just do the following one line.
|
|
83
83
|
|
|
@@ -101,8 +101,8 @@ Here are a few ways to create a blank text file:
|
|
|
101
101
|
Now, in your blank window, type in the following:
|
|
102
102
|
|
|
103
103
|
{{{
|
|
104
|
+
require 'green_shoes'
|
|
104
105
|
Shoes.app do
|
|
105
|
-
background "#DFA"
|
|
106
106
|
para "Welcome to Shoes"
|
|
107
107
|
end
|
|
108
108
|
}}}
|
metadata
CHANGED
|
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
|
4
4
|
prerelease: false
|
|
5
5
|
segments:
|
|
6
6
|
- 0
|
|
7
|
-
-
|
|
7
|
+
- 201
|
|
8
8
|
- 0
|
|
9
|
-
version: 0.
|
|
9
|
+
version: 0.201.0
|
|
10
10
|
platform: ruby
|
|
11
11
|
authors:
|
|
12
12
|
- ashbb
|
|
@@ -14,7 +14,7 @@ autorequire:
|
|
|
14
14
|
bindir: bin
|
|
15
15
|
cert_chain: []
|
|
16
16
|
|
|
17
|
-
date: 2011-
|
|
17
|
+
date: 2011-04-04 00:00:00 +09:00
|
|
18
18
|
default_executable:
|
|
19
19
|
dependencies:
|
|
20
20
|
- !ruby/object:Gem::Dependency
|
|
@@ -194,6 +194,7 @@ files:
|
|
|
194
194
|
- samples/sample48.rb
|
|
195
195
|
- samples/sample49.rb
|
|
196
196
|
- samples/sample5.rb
|
|
197
|
+
- samples/sample50.rb
|
|
197
198
|
- samples/sample6.rb
|
|
198
199
|
- samples/sample7.rb
|
|
199
200
|
- samples/sample8.rb
|
|
@@ -250,6 +251,7 @@ files:
|
|
|
250
251
|
- snapshots/sample48.png
|
|
251
252
|
- snapshots/sample49.png
|
|
252
253
|
- snapshots/sample5.png
|
|
254
|
+
- snapshots/sample50.png
|
|
253
255
|
- snapshots/sample6.png
|
|
254
256
|
- snapshots/sample7.png
|
|
255
257
|
- snapshots/sample8.png
|