kaki-lifegame 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d8b005dd3f21e84190a592064607d3c57d7d1b77
4
+ data.tar.gz: cfe64c5f8f91ad79834da2fc3499ed7c32144e0b
5
+ SHA512:
6
+ metadata.gz: ad313461534ff9031a7d55804bd180d2f9a5aa33fd7fea769f63bd7ac47f87ea4f3480364d577e73ebec52cb7483b8bd2daf5a89f39b0a603466b544ac7bb845
7
+ data.tar.gz: 1a6e554edd145762b260af06c8cbf15d6182d90888f6f0e109204ed040de35bb2398b979a43dcdd3b7475dcfa7148108be9b48490dc17184c712f2cc4a134404
@@ -0,0 +1,40 @@
1
+ ## 概要
2
+
3
+ いわゆる「コンウェイのライフゲーム」です。GTK2を使ったフィールド・エディタが付いています。
4
+
5
+ ## 実行
6
+
7
+ 以下のコマンドでエディタが立ち上がります。
8
+
9
+ ```
10
+ $ kaki-lifegame
11
+ ```
12
+
13
+ ## 機能
14
+
15
+ - 開始
16
+ - 実行します。
17
+ - 停止
18
+ - 停止します。以下の機能はすべて停止中にのみ有効です。
19
+ - 1ステップ進める
20
+ - ステップ実行です。
21
+ - ばらまく
22
+ - 適当にセルを散布します。
23
+ - 格子
24
+ - 格子の表示を切り替えます。
25
+ - 緑色/橙色
26
+ - セルの色です。
27
+ - 全クリア
28
+ - すべてのセルを消します。
29
+ - 一時保存
30
+ - 一時的にフィールドを内部に保存します。
31
+ - 復帰
32
+ - 内部に保存したデータをフィールドに戻します。
33
+ - ファイルに保存
34
+ - フィールドをファイルに保存します。
35
+ - ファイルの読み込み
36
+ - ファイルに保存したフィールドのデータを読み込んで表示します。
37
+ - 終了
38
+ - 終了します。
39
+
40
+
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'kaki-lifegame'
3
+
4
+ LifeGame.start
@@ -0,0 +1,434 @@
1
+ require 'gtk2'
2
+
3
+ module LifeGame
4
+ class Field
5
+ Width, Height = 85, 60
6
+ MG = 4
7
+
8
+ def initialize
9
+ clear
10
+ @step = @stored_step = 0
11
+ @reserved = copy
12
+ end
13
+ attr_reader :step
14
+
15
+ def set_cell(x, y)
16
+ @field[y + MG][x + MG] = 1
17
+ end
18
+
19
+ def reset_cell(x, y)
20
+ @field[y + MG][x + MG] = 0
21
+ end
22
+
23
+ def get_cell(x, y)
24
+ @field[y + MG][x + MG]
25
+ end
26
+
27
+ def next
28
+ @before = copy
29
+ nxf = new_field
30
+ each_cell do |x, y|
31
+ n = alive_cells(x, y)
32
+ if get_cell(x, y).zero?
33
+ nxf[y + MG][x + MG] = 1 if n == 3
34
+ else
35
+ nxf[y + MG][x + MG] = 1 if n == 2 or n == 3
36
+ end
37
+ end
38
+ @field = nxf
39
+ end
40
+
41
+ def alive_cells(x, y)
42
+ get_cell(x - 1, y - 1) + get_cell(x, y - 1) + get_cell(x + 1, y - 1) +
43
+ get_cell(x - 1, y) + get_cell(x + 1, y) +
44
+ get_cell(x - 1, y + 1) + get_cell(x, y + 1) + get_cell(x + 1, y + 1)
45
+ end
46
+
47
+ def each_cell
48
+ (Height + (MG - 1) * 2).times do |y|
49
+ (Width + (MG - 1) * 2).times {|x| yield(x - MG + 1, y - MG + 1)}
50
+ end
51
+ end
52
+
53
+ def new_field
54
+ Array.new(Height + MG * 2) {Array.new(Width + MG * 2, 0)}
55
+ end
56
+
57
+ def copy
58
+ copied = new_field
59
+ each_cell do |x, y|
60
+ copied[y + MG][x + MG] = get_cell(x, y)
61
+ end
62
+ copied
63
+ end
64
+
65
+ def renewal?(x, y)
66
+ @before[y + MG][x + MG] != get_cell(x, y)
67
+ end
68
+
69
+ def preserve
70
+ @reserved = copy
71
+ @stored_step = @step
72
+ end
73
+
74
+ def restore
75
+ @field = @reserved
76
+ @step = @stored_step
77
+ end
78
+
79
+ def clear
80
+ @field = Array.new(Height + MG * 2) {Array.new(Width + MG * 2, 0)}
81
+ end
82
+
83
+ def load(file_name)
84
+ @field = []
85
+ open(file_name, "r") do |io|
86
+ @step = io.gets.chomp.scan(/\d+/)[0].to_i
87
+ io.each_line do |line|
88
+ @field << line.chomp.split(",").map(&:to_i)
89
+ end
90
+ end
91
+ end
92
+
93
+ def save(file_name)
94
+ open(file_name, "w") do |io|
95
+ io.puts "Step : #{@step}"
96
+ @field.each {|x| io.puts x.map(&:to_s).join(",")}
97
+ end
98
+ end
99
+
100
+ def count
101
+ @step += 1
102
+ end
103
+
104
+ def step_reset
105
+ @step = 0
106
+ end
107
+ end
108
+
109
+ class FieldArea < Gtk::DrawingArea
110
+ CellSize = 11
111
+ Space = 1
112
+
113
+ L = CellSize + Space * 2
114
+ AreaW, AreaH = Field::Width * L, Field::Height * L
115
+
116
+ def initialize(field)
117
+ super()
118
+ set_size_request(AreaW, AreaH)
119
+
120
+ @f = field
121
+
122
+ @colormap = Gdk::Colormap.system
123
+ @color = {black: Gdk::Color.new(0, 0, 0), green: Gdk::Color.new(0xc784, 0xffff, 0x52c4),
124
+ orange: Gdk::Color.new(0xffff, 0xbb85, 0xf7a),
125
+ grid_color: Gdk::Color.new(0x4b79, 0x4b79, 0x4b79)}
126
+ @color.each {|_, v| @colormap.alloc_color(v, false, true)}
127
+ @cell_color = :green
128
+
129
+ @grid = false
130
+
131
+ signal_connect("expose_event") do
132
+ @gc = Gdk::GC.new(window)
133
+ set_color(:black)
134
+ window.draw_rectangle(@gc, true, 0, 0, AreaW, AreaH)
135
+ end
136
+
137
+ @time_goes = false
138
+ add_events(Gdk::Event::BUTTON_PRESS_MASK | Gdk::Event::BUTTON_MOTION_MASK)
139
+
140
+ signal_connect("button_press_event") do |w, e|
141
+ unless @time_goes
142
+ x, y = e.x.to_i / L, e.y.to_i / L
143
+ c = @f.get_cell(x, y)
144
+ c.zero? ? set_cell(x, y) : reset_cell(x, y)
145
+ end
146
+ end
147
+
148
+ signal_connect('motion_notify_event') do |w, e|
149
+ unless @time_goes
150
+ x, y = e.x.to_i / L, e.y.to_i / L
151
+ set_cell(x, y) if e.state.button1_mask?
152
+ reset_cell(x, y) if e.state.button3_mask?
153
+ end
154
+ end
155
+
156
+ Gtk.timeout_add(500) do
157
+ go_on_one_step if @time_goes
158
+ true
159
+ end
160
+ end
161
+ attr_writer :time_goes, :label
162
+
163
+ def set_color(color_name)
164
+ @gc.rgb_fg_color = @color[color_name]
165
+ end
166
+
167
+ def set_cell(x, y)
168
+ set_color(@cell_color)
169
+ @f.set_cell(x, y)
170
+ window.draw_rectangle(@gc, true, L * x + Space, L * y + Space, CellSize, CellSize)
171
+ end
172
+
173
+ def reset_cell(x, y)
174
+ set_color(:black)
175
+ @f.reset_cell(x, y)
176
+ window.draw_rectangle(@gc, true, L * x + Space, L * y + Space, CellSize, CellSize)
177
+ end
178
+
179
+ def go_on_one_step
180
+ @f.next
181
+
182
+ each_cell do |x, y|
183
+ renewal_one_place(x, y) if @f.renewal?(x, y)
184
+ end
185
+
186
+ @f.count
187
+ show_step
188
+ end
189
+
190
+ def renewal_one_place(x, y)
191
+ @f.get_cell(x, y).zero? ? reset_cell(x, y) : set_cell(x, y)
192
+ end
193
+
194
+ def preserve
195
+ @f.preserve
196
+ end
197
+
198
+ def restore
199
+ @f.restore
200
+ redraw
201
+ show_step
202
+ end
203
+
204
+ def show_step
205
+ @label.set_text("Step : #{@f.step}")
206
+ end
207
+
208
+ def redraw
209
+ each_cell {|x, y| renewal_one_place(x, y)}
210
+ end
211
+
212
+ def each_cell
213
+ Field::Height.times do |y|
214
+ Field::Width.times {|x| yield(x, y)}
215
+ end
216
+ end
217
+
218
+ def clear
219
+ @f.clear
220
+ redraw
221
+ @f.step_reset
222
+ show_step
223
+ end
224
+
225
+ def scatter
226
+ 100.times do
227
+ x, y = rand(Field::Width), rand(Field::Height)
228
+ set_cell(x, y) if @f.get_cell(x, y).zero?
229
+ end
230
+ end
231
+
232
+ def save_file(file_name)
233
+ @f.save(file_name)
234
+ end
235
+
236
+ def load_file(file_name)
237
+ @f.load(file_name)
238
+ redraw
239
+ show_step
240
+ end
241
+
242
+ def grid
243
+ @grid = !@grid
244
+ set_color(@grid ? :grid_color : :black)
245
+ window.draw_rectangle(@gc, true, 0, 0, AreaW, AreaH)
246
+ redraw
247
+ end
248
+
249
+ def set_cell_color(color)
250
+ @cell_color = color
251
+ redraw
252
+ end
253
+ end
254
+
255
+ class SideBar < Gtk::VBox
256
+ def initialize(field_area)
257
+ super()
258
+ set_size_request(130, FieldArea::AreaH)
259
+ @farea = field_area
260
+ @edit_mode = true
261
+
262
+ set_box1
263
+ set_box2
264
+ set_box3
265
+ set_box4
266
+ set_box5
267
+ end
268
+
269
+ def set_box1
270
+ start_bt = Gtk::Button.new("開始")
271
+ stop_bt = Gtk::Button.new("停止")
272
+ step_bt = Gtk::Button.new("1ステップ進める")
273
+
274
+ start_bt.signal_connect("clicked") do
275
+ @edit_mode = false
276
+ @farea.time_goes = true
277
+ end
278
+
279
+ stop_bt.signal_connect("clicked") do
280
+ @edit_mode = true
281
+ @farea.time_goes = false
282
+ end
283
+
284
+ step_bt.signal_connect("clicked") do
285
+ @farea.go_on_one_step if @edit_mode
286
+ end
287
+
288
+ l = Gtk::Label.new("")
289
+ @farea.label = l
290
+
291
+ box = Gtk::VBox.new
292
+ pack_start(box, false, false, 0)
293
+ box.pack_start(start_bt, false, false, 5)
294
+ box.pack_start(stop_bt , false, false, 5)
295
+ box.pack_start(step_bt , false, false, 5)
296
+ box.pack_start(l, false, false, 5)
297
+ box.pack_start(Gtk::HSeparator.new, false, false, 10)
298
+ end
299
+
300
+ def set_box2
301
+ scatter_bt = Gtk::Button.new("ばらまく")
302
+ grid_bt = Gtk::Button.new("格子")
303
+ green_bt = Gtk::Button.new("緑色")
304
+ orange_bt = Gtk::Button.new("橙色")
305
+ clear_bt = Gtk::Button.new("全クリア")
306
+
307
+ scatter_bt.signal_connect("clicked") do
308
+ @farea.scatter if @edit_mode
309
+ end
310
+
311
+ grid_bt.signal_connect("clicked") do
312
+ @farea.grid if @edit_mode
313
+ end
314
+
315
+ green_bt.signal_connect("clicked") do
316
+ @farea.set_cell_color(:green) if @edit_mode
317
+ end
318
+
319
+ orange_bt.signal_connect("clicked") do
320
+ @farea.set_cell_color(:orange) if @edit_mode
321
+ end
322
+
323
+ clear_bt.signal_connect("clicked") do
324
+ @farea.clear if @edit_mode
325
+ end
326
+
327
+ small_box = Gtk::HBox.new
328
+ small_box.pack_start(green_bt , true, true, 1)
329
+ small_box.pack_start(orange_bt, true, true, 1)
330
+
331
+ box = Gtk::VBox.new
332
+ pack_start(box, false, false, 0)
333
+ box.pack_start(scatter_bt, false, false, 5)
334
+ box.pack_start(grid_bt , false, false, 5)
335
+ box.pack_start(small_box , false, false, 5)
336
+ box.pack_start(clear_bt , false, false, 5)
337
+ box.pack_start(Gtk::HSeparator.new, false, false, 10)
338
+ end
339
+
340
+ def set_box3
341
+ preserve_bt = Gtk::Button.new("一時保存")
342
+ restore_bt = Gtk::Button.new("復帰")
343
+
344
+ preserve_bt.signal_connect("clicked") do
345
+ @farea.preserve if @edit_mode
346
+ end
347
+
348
+ restore_bt.signal_connect("clicked") do
349
+ @farea.restore if @edit_mode
350
+ end
351
+
352
+ box = Gtk::VBox.new
353
+ pack_start(box, false, false, 0)
354
+ box.pack_start(preserve_bt, false, false, 5)
355
+ box.pack_start(restore_bt, false, false, 5)
356
+ box.pack_start(Gtk::HSeparator.new, false, false, 10)
357
+ end
358
+
359
+ def set_box4
360
+ save_bt = Gtk::Button.new("ファイルに保存")
361
+ load_bt = Gtk::Button.new("ファイルの読み込み")
362
+
363
+ save_bt.signal_connect("clicked") do
364
+ if @edit_mode
365
+ file_name = select_file("Save File", Gtk::FileChooser::ACTION_SAVE)
366
+ @farea.save_file(file_name) if file_name
367
+ end
368
+ end
369
+
370
+ load_bt.signal_connect("clicked") do
371
+ if @edit_mode
372
+ file_name = select_file("Load File", Gtk::FileChooser::ACTION_OPEN)
373
+ @farea.load_file(file_name) if file_name
374
+ end
375
+ end
376
+
377
+ box = Gtk::VBox.new
378
+ pack_start(box, false, false, 0)
379
+ box.pack_start(save_bt, false, false, 5)
380
+ box.pack_start(load_bt, false, false, 5)
381
+ box.pack_start(Gtk::HSeparator.new, false, false, 10)
382
+ end
383
+
384
+ def set_box5
385
+ close_bt = Gtk::Button.new("終了")
386
+
387
+ close_bt.signal_connect("clicked") do
388
+ Gtk.main_quit if @edit_mode
389
+ end
390
+
391
+ box = Gtk::VBox.new
392
+ pack_start(box, false, false, 0)
393
+ box.pack_start(close_bt, false, false, 5)
394
+ end
395
+
396
+ def select_file(title, mode)
397
+ file_name = nil
398
+ dialog = Gtk::FileChooserDialog.new(title,
399
+ nil, mode, nil,
400
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
401
+ [Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])
402
+ if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
403
+ file_name = dialog.filename
404
+ end
405
+ dialog.destroy
406
+ file_name
407
+ end
408
+ end
409
+
410
+ class MainWindow < Gtk::Window
411
+ def initialize
412
+ super("Life Game")
413
+ set_resizable(false)
414
+
415
+ field = Field.new
416
+ field_area = FieldArea.new(field)
417
+ side_bar = SideBar.new(field_area)
418
+
419
+ box = Gtk::HBox.new
420
+ add(box)
421
+ box.pack_start(field_area, true, true, 0)
422
+ box.pack_start(side_bar , true, true, 0)
423
+
424
+ signal_connect("destroy") {Gtk.main_quit}
425
+ show_all
426
+ end
427
+ end
428
+
429
+ def self.start
430
+ MainWindow.new
431
+ Gtk.main
432
+ end
433
+ end
434
+
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kaki-lifegame
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - obelisk
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-04-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: gtk2
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Conway's Game of Life with GTK+ Field Editor
28
+ email: obelisk_1968@mail.goo.ne.jp
29
+ executables:
30
+ - kaki-lifegame
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - README.md
35
+ - bin/kaki-lifegame
36
+ - lib/kaki-lifegame.rb
37
+ homepage:
38
+ licenses:
39
+ - MIT
40
+ metadata: {}
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubyforge_project:
57
+ rubygems_version: 2.5.2
58
+ signing_key:
59
+ specification_version: 4
60
+ summary: Conway's Game of Life
61
+ test_files: []