conway_deathmatch 0.0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4d28b8f025d9ac021d8d3b470733d5e7ceec6855
4
+ data.tar.gz: 1410214a78a7fa36ff54da8dcb30490600e90d8c
5
+ SHA512:
6
+ metadata.gz: 4136f08c1a086e3caaded1ce6973bba424ea183eab29b4054e399b1a16a5a1718783d96f53a2dd06a41a2af37dd112ae8485311ab49daf0cd39788d6b44ad4d5
7
+ data.tar.gz: bd31468226a5b4bb3d01fe3848f0f6569099bca4b1ac1b507b3ab9cb8be0ff0d10d07bda87bb969b7e0d62543b3fba0497ba886ec28901cf717c22406a7a5ef9
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # process cmdline options
4
+ #
5
+ require 'slop'
6
+ opts = Slop.parse(help: true,
7
+ banner: true,
8
+ strict: true,
9
+ optional_arguments: true) do
10
+ banner 'Usage: conway_deathmatch [options]'
11
+
12
+ on 'w', 'width=', '[int] Board width', as: Integer
13
+ on 'height=', '[int] Board height', as: Integer
14
+ on 'n', 'num_ticks=', '[int] Max number of ticks to generate', as: Integer
15
+ on 's', 'sleep=', '[flt] Sleep duration', as: Float
16
+ on 'p', 'points=', '[str] e.g. "acorn 50 18 p 1 2 p 3 4"', as: String
17
+ on 'S', 'step', 'Hold ticks for user input'
18
+ on 'silent', 'Only render the final state'
19
+ on 'm', 'multiplayer', 'Use multiplayer evaluation'
20
+ end
21
+ width = opts[:width] || 70
22
+ height = opts[:height] || 40
23
+ # assumes 1P, TODO: figure out MP
24
+ shapes = opts[:points] || "acorn 50 18"
25
+ slp = opts[:sleep] || 0.02
26
+ n = opts[:num_ticks]
27
+ render_continuous = (n.nil? or !opts.silent?)
28
+
29
+
30
+ # create game
31
+ #
32
+ require 'conway_deathmatch'
33
+ require 'conway_deathmatch/shapes'
34
+
35
+ include ConwayGame
36
+ b = BoardState.new(width, height)
37
+ Shapes.add(b, shapes) # assumes 1P
38
+ b.multiplayer = opts.multiplayer?
39
+
40
+ # play game
41
+ #
42
+ count = 0
43
+ while n.nil? or count <= n
44
+ if render_continuous
45
+ puts
46
+ puts count
47
+ puts b.render
48
+ end
49
+
50
+ b.tick
51
+
52
+ if opts.step?
53
+ gets
54
+ else
55
+ sleep slp if slp > 0.0
56
+ end
57
+ count += 1
58
+ end
59
+
60
+ # finish
61
+ #
62
+ if n and opts.silent?
63
+ puts
64
+ puts count
65
+ puts b.render
66
+ end
@@ -0,0 +1,175 @@
1
+ acorn:
2
+ - [0, 2]
3
+ - [1, 0]
4
+ - [1, 2]
5
+ - [3, 1]
6
+ - [4, 2]
7
+ - [5, 2]
8
+ - [6, 2]
9
+
10
+ beacon:
11
+ - [0, 0]
12
+ - [0, 1]
13
+ - [1, 0]
14
+ - [2, 3]
15
+ - [3, 2]
16
+ - [3, 3]
17
+
18
+ beehive:
19
+ - [0, 1]
20
+ - [1, 0]
21
+ - [1, 2]
22
+ - [2, 0]
23
+ - [2, 2]
24
+ - [3, 1]
25
+
26
+ blinker:
27
+ - [0, 1]
28
+ - [1, 1]
29
+ - [2, 1]
30
+
31
+ block:
32
+ - [0, 0]
33
+ - [0, 1]
34
+ - [1, 0]
35
+ - [1, 1]
36
+
37
+ block_engine_count:
38
+ - [0, 5]
39
+ - [2, 4]
40
+ - [2, 5]
41
+ - [4, 1]
42
+ - [4, 2]
43
+ - [4, 3]
44
+ - [6, 0]
45
+ - [6, 1]
46
+ - [6, 2]
47
+ - [7, 1]
48
+
49
+ block_engine_space:
50
+ - [0, 0]
51
+ - [0, 1]
52
+ - [1, 0]
53
+ - [1, 3]
54
+ - [2, 0]
55
+ - [2, 3]
56
+ - [2, 4]
57
+ - [3, 2]
58
+ - [4, 0]
59
+ - [4, 2]
60
+ - [4, 3]
61
+ - [4, 4]
62
+
63
+ block_engine_stripe:
64
+ - [0, 0]
65
+ - [1, 0]
66
+ - [2, 0]
67
+ - [3, 0]
68
+ - [4, 0]
69
+ - [5, 0]
70
+ - [6, 0]
71
+ - [7, 0]
72
+ - [9, 0]
73
+ - [10, 0]
74
+ - [11, 0]
75
+ - [12, 0]
76
+ - [13, 0]
77
+ - [17, 0]
78
+ - [18, 0]
79
+ - [19, 0]
80
+ - [26, 0]
81
+ - [27, 0]
82
+ - [28, 0]
83
+ - [29, 0]
84
+ - [30, 0]
85
+ - [31, 0]
86
+ - [32, 0]
87
+ - [34, 0]
88
+ - [35, 0]
89
+ - [36, 0]
90
+ - [37, 0]
91
+ - [38, 0]
92
+
93
+ boat:
94
+ - [0, 0]
95
+ - [0, 1]
96
+ - [1, 0]
97
+ - [1, 2]
98
+ - [2, 1]
99
+
100
+ die_hard:
101
+ - [0, 1]
102
+ - [1, 1]
103
+ - [1, 2]
104
+ - [5, 2]
105
+ - [6, 0]
106
+ - [6, 2]
107
+ - [7, 2]
108
+
109
+ glider:
110
+ - [0, 2]
111
+ - [1, 0]
112
+ - [1, 2]
113
+ - [2, 1]
114
+ - [2, 2]
115
+
116
+ loaf:
117
+ - [0, 1]
118
+ - [1, 0]
119
+ - [1, 2]
120
+ - [2, 0]
121
+ - [2, 3]
122
+ - [3, 1]
123
+ - [3, 2]
124
+
125
+ lwss:
126
+ - [0, 1]
127
+ - [0, 3]
128
+ - [1, 0]
129
+ - [2, 0]
130
+ - [3, 0]
131
+ - [3, 3]
132
+ - [4, 0]
133
+ - [4, 1]
134
+ - [4, 2]
135
+
136
+ rpent:
137
+ - [0, 1]
138
+ - [1, 0]
139
+ - [1, 1]
140
+ - [1, 2]
141
+ - [2, 0]
142
+
143
+ swastika:
144
+ - [0, 0]
145
+ - [0, 1]
146
+ - [0, 2]
147
+ - [0, 3]
148
+ - [0, 6]
149
+ - [1, 3]
150
+ - [1, 6]
151
+ - [2, 3]
152
+ - [2, 6]
153
+ - [3, 0]
154
+ - [3, 1]
155
+ - [3, 2]
156
+ - [3, 4]
157
+ - [3, 5]
158
+ - [3, 6]
159
+ - [4, 0]
160
+ - [4, 3]
161
+ - [5, 0]
162
+ - [5, 3]
163
+ - [6, 0]
164
+ - [6, 3]
165
+ - [6, 4]
166
+ - [6, 5]
167
+ - [6, 6]
168
+
169
+ toad:
170
+ - [0, 1]
171
+ - [0, 2]
172
+ - [1, 3]
173
+ - [2, 0]
174
+ - [3, 1]
175
+ - [3, 2]
@@ -0,0 +1,35 @@
1
+ require 'conway_deathmatch'
2
+ require 'yaml'
3
+
4
+ module ConwayGame
5
+ module Shapes
6
+ @@known
7
+
8
+ # memoize data/shapes.yaml
9
+ def self.known
10
+ @@known ||= YAML.load_file(File.join(__dir__, 'data', 'shapes.yaml'))
11
+ end
12
+
13
+ # parse a string like "acorn 12 22 block 5 0 p 1 2 p 3 4 p 56 78"
14
+ # add known shapes
15
+ def self.add(board, str, val = BoardState::ALIVE)
16
+ tokens = str.split
17
+ points = []
18
+ known = self.known
19
+
20
+ while !tokens.empty?
21
+ shape = tokens.shift.downcase
22
+ raise "no coordinates for #{shape}" if tokens.length < 2
23
+ x = tokens.shift.to_i
24
+ y = tokens.shift.to_i
25
+ case shape.downcase
26
+ when 'p'
27
+ points << [x, y]
28
+ else
29
+ board.add_points(known.fetch(shape), x, y, val)
30
+ end
31
+ end
32
+ board.add_points(points, 0, 0, val)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,120 @@
1
+ module ConwayGame; end # create namespace
2
+
3
+ # data structure for the board - 2d array
4
+ # implements standard and multiplayer evaluation
5
+ # static boundaries are treated as dead
6
+ #
7
+ class ConwayGame::BoardState
8
+ class BoundsError < RuntimeError; end
9
+
10
+ DEAD = '.'
11
+ ALIVE = '0'
12
+
13
+ def self.new_state(x_len, y_len)
14
+ state = []
15
+ x_len.times { state << Array.new(y_len, DEAD) }
16
+ state
17
+ end
18
+
19
+ attr_accessor :multiplayer
20
+
21
+ def initialize(x_len, y_len)
22
+ # ranges, yay! (exclude_end)
23
+ @xr = (0...x_len)
24
+ @yr = (0...y_len)
25
+ @state = self.class.new_state(x_len, y_len)
26
+ @multiplayer = false
27
+ end
28
+
29
+ # Conway's Game of Life transition rules
30
+ def next_value(x, y)
31
+ n, birthright = neighbor_stats(x, y)
32
+ if alive?(x, y)
33
+ (n == 2 or n == 3) ? birthright : DEAD
34
+ else
35
+ (n == 3) ? birthright : DEAD
36
+ end
37
+ end
38
+
39
+ def in_bounds?(x, y)
40
+ @xr.include?(x) and @yr.include?(y)
41
+ end
42
+
43
+ def in_bounds!(x, y)
44
+ raise(BoundsError, "(#{x}, #{y}) (#{@xr}, #{@yr})") unless in_bounds?(x, y)
45
+ end
46
+
47
+ # out of bounds considered dead
48
+ def alive?(x, y)
49
+ in_bounds?(x, y) and @state[x][y] != DEAD
50
+ end
51
+
52
+ # population of each neighbor
53
+ def neighbor_population(x, y)
54
+ neighbors = Hash.new(0)
55
+ (x-1..x+1).each { |xn|
56
+ (y-1..y+1).each { |yn|
57
+ if alive?(xn, yn) and !(xn == x and yn == y) # don't count self
58
+ neighbors[@state[xn][yn]] += 1
59
+ end
60
+ }
61
+ }
62
+ neighbors
63
+ end
64
+
65
+ # multiplayer, neighbor count and birthright
66
+ def neighbor_stats(x, y)
67
+ if @multiplayer
68
+ total = 0
69
+ largest = 0
70
+ birthright = nil
71
+ neighbor_population(x, y).each { |sym, cnt|
72
+ total += cnt
73
+ if cnt > largest
74
+ largest = cnt
75
+ birthright = sym
76
+ end
77
+ }
78
+ [total, birthright]
79
+ else
80
+ [neighbor_population(x, y).values.reduce(:+), ALIVE]
81
+ end
82
+ end
83
+
84
+ # generate the next state table
85
+ def tick
86
+ new_state = self.class.new_state(@xr.last, @yr.last)
87
+ @xr.each { |x| @yr.each { |y| new_state[x][y] = next_value(x, y) } }
88
+ @state = new_state
89
+ self
90
+ end
91
+
92
+ # set a single point, raise on OOB
93
+ def populate(x, y, val = ALIVE)
94
+ in_bounds!(x, y)
95
+ @state[x][y] = val
96
+ end
97
+
98
+ # set several points (2d array), ignore OOB
99
+ def add_points(points, x_off = 0, y_off = 0, val = ALIVE)
100
+ points.each { |point|
101
+ x = point[0] + x_off
102
+ y = point[1] + y_off
103
+ @state[x][y] = val if self.in_bounds?(x, y)
104
+ }
105
+ self
106
+ end
107
+
108
+ # for line-based text output, iterate over y-values first (i.e. per row)
109
+ def render_text
110
+ @state.transpose.map { |row| row.join }.join("\n")
111
+ end
112
+ alias_method :render, :render_text
113
+
114
+ # full board scan
115
+ def population
116
+ population = Hash.new(0)
117
+ @state.each { |col| col.each { |val| population[val] += 1 } }
118
+ population
119
+ end
120
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: conway_deathmatch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Rick Hull
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-24 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Deathmatch
14
+ email:
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - bin/conway_deathmatch
20
+ - lib/conway_deathmatch.rb
21
+ - lib/conway_deathmatch/data/shapes.yaml
22
+ - lib/conway_deathmatch/shapes.rb
23
+ homepage:
24
+ licenses:
25
+ - GPL
26
+ metadata: {}
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubyforge_project:
43
+ rubygems_version: 2.2.2
44
+ signing_key:
45
+ specification_version: 4
46
+ summary: Conway's Game of Life
47
+ test_files: []