kaki-lifegame 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.
@@ -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: []