1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
|
#! /usr/bin/env ruby
# frozen_string_literal: true
require 'colonial_twilight/spaces'
module ColonialTwilight
class Board
FRANCE_TRACK = %w[A B C D E F].freeze
TRACKS = %i[support_commitment opposition_bases fln_resources gov_resources commitment france_track border_zone_track].freeze
attr_reader :spaces
%i[commitment gov_resources fln_resources france_track border_zone_track
support_commitment opposition_bases].each do |sym|
define_method(sym) { instance_variable_get("@#{sym}").v }
end
%i[gov_bases french_troops french_police algerian_troops algerian_police fln_bases fln_underground].each do |sym|
define_method("available_#{sym}") { @available.send(sym) }
# define_method "casualties_#{sym}" do @casualties.send(sym) end
# define_method "out_of_play_#{sym}" do @out_of_play.send(sym) end
end
def initialize
@spaces = []
@capabilities = []
@available = Box.new :available
@casualties = Box.new :casualties
@out_of_play = Box.new :out_of_play
@support_commitment = Track.new 50
@opposition_bases = Track.new 50
@fln_resources = Track.new 50
@gov_resources = Track.new 50
@commitment = Track.new 50
@france_track = Track.new 5
@border_zone_track = Track.new 4
set_spaces
set_adjacents
end
def inspect
'Board'
end
def load(scenario)
case scenario
when :short then short
when :medium then medium
when :full then full
else raise "unknown scenario : #{scenario}"
end
end
def by_name(name)
@spaces.find { |s| s.name == name }
end
def sector
@spaces.select(&:sector?)
end
alias sectors sector
def city
@spaces.select(&:city?)
end
alias cities city
def country
@spaces.select(&:country?)
end
alias countries country
def has(where = :spaces, &block)
!send(where).select(&block).empty?
end
def search(where = :spaces, &block)
send(where).select(&block)
end
def count(where = :spaces, &block)
send(where).inject(0) { |i, s| i + block.call(s) }
end
def compute_opposition_bases
count { |s| s.oppose? ? s.pop : 0 } + count(&:fln_bases)
end
def compute_support_commitment
count { |s| s.support? ? s.pop : 0 } + @commitment.v
end
def shift(space, towards, num = 1)
num.times { space.shift towards }
end
def shift_track(what, amount)
raise "unknown track : #{what}" unless TRACKS.include? what
instance_variable_get("@#{what}").shift amount
end
def apply(action)
action.steps.each do |step|
case step[:kind]
when :transfer then transfer(step)
else raise "unknow action step #{step}"
end
end
end
# def terror(where, num)
# where.terror += num
# end
# def data
# h = {}
# %i[gov_resources fln_resources commitment support_commitment opposition_bases
# france_track border_zone_track available casualties out_of_play].each do |sym|
# h[sym] = instance_variable_get("@#{sym}").data
# end
# h[:capabilities] = @capabilities
# h[:spaces] = @spaces.inject([]) { |a, s| a << s.data }
# h
# end
private
def transfer(data)
src = get_obj(data[:src])
dst = get_obj(data[:dst])
src.add data[:what], -data[:num]
dst.add flip?(data), data[:num]
end
def flip?(data)
!data[:flip] ? data[:what] : data[:flip]
end
def get_obj(obj)
return obj if obj.is_a? ColonialTwilight::Sector
case obj
when :available then @available
when :casualties then @casualties
when :out_of_play then @out_of_play
else
raise "unknown Board variable named #{obj}"
end
end
def add(kls, *args)
@spaces << kls.new(*args)
end
def set_spaces
mountain = Sector::MOUNTAIN
border = Sector::BORDER
coastal = Sector::COASTAL
add Sector, 'Barika', 'I', 1, 1, mountain
add Sector, 'Batna', 'I', 2, 0, mountain
add Sector, 'Biskra', 'I', 3, 0, border
add Sector, 'Oum El Bouaghi', 'I', 4, 0, mountain
add Sector, 'Tebessa', 'I', 5, 1, mountain | border
add Sector, 'Negrine', 'I', 6, 0, mountain | border
add City, 'Constantine', 'II', 2
add Sector, 'Setif', 'II', 1, 1, mountain | coastal
add Sector, 'Philippeville', 'II', 2, 2, mountain | coastal
add Sector, 'Souk Ahras', 'II', 3, 2, coastal | border
add Sector, 'Tizi Ouzou', 'III', 1, 2, mountain | coastal
add Sector, 'Bordj Bou Arreridj', 'III', 2, 1, mountain
add Sector, 'Bougie', 'III', 3, 2, mountain | coastal
add City, 'Algiers', 'IV', 3, coastal
add Sector, 'Medea', 'IV', 1, 2, mountain | coastal
add Sector, 'Orleansville', 'IV', 2, 2, mountain | coastal
add City, 'Oran', 'V', 2, coastal
add Sector, 'Mecheria', 'V', 1, 0, mountain | border
add Sector, 'Tlemcen', 'V', 2, 1, border | coastal
add Sector, 'Sidi Bel Abbes', 'V', 3, 1, coastal
add Sector, 'Mostaganem', 'V', 4, 2, mountain | coastal
add Sector, 'Saida', 'V', 5, 0, mountain
add Sector, 'Mascara', 'V', 6, 0, mountain
add Sector, 'Tiaret', 'V', 7, 0, mountain
add Sector, 'Ain Sefra', 'V', 8, 0, border
add Sector, 'Laghouat', 'V', 9, 0
add Sector, 'Sidi Aissa', 'VI', 1, 0, mountain
add Sector, 'Ain Oussera', 'VI', 2, 1, mountain
add Country, 'Morocco'
add Country, 'Tunisia'
end
def adjacents(name, *args)
by_name(name).adjacents = args
end
def set_adjacents
adjacents 'Barika', 1, 2, 3, 7, 8, 11, 26
adjacents 'Batna', 0, 2, 3, 5
adjacents 'Biskra', 0, 1, 5, 25, 26, 29
adjacents 'Oum El Bouaghi', 0, 1, 4, 5, 8, 9
adjacents 'Tebessa', 3, 5, 9, 29
adjacents 'Negrine', 1, 2, 3, 4, 29
adjacents 'Constantine', 7, 8
adjacents 'Setif', 0, 6, 8, 11, 12
adjacents 'Philippeville', 0, 3, 7, 6, 9
adjacents 'Souk Ahras', 3, 4, 8, 29
adjacents 'Tizi Ouzou', 11, 12, 14
adjacents 'Bordj Bou Arreridj', 0, 7, 10, 12, 14, 26
adjacents 'Bougie', 7, 10, 11
adjacents 'Algiers', 14
adjacents 'Medea', 10, 11, 13, 15, 26, 27
adjacents 'Orleansville', 14, 20, 23, 27
adjacents 'Oran', 19
adjacents 'Mecheria', 18, 21, 24, 28
adjacents 'Tlemcen', 17, 19, 21, 28
adjacents 'Sidi Bel Abbes', 16, 18, 20, 21, 22
adjacents 'Mostaganem', 15, 19, 22, 23
adjacents 'Saida', 17, 18, 19, 22, 24
adjacents 'Mascara', 19, 20, 21, 23, 24
adjacents 'Tiaret', 15, 20, 22, 24, 27
adjacents 'Ain Sefra', 17, 21, 22, 23, 25, 27, 28
adjacents 'Laghouat', 2, 24, 26, 27
adjacents 'Sidi Aissa', 0, 2, 11, 14, 25, 27
adjacents 'Ain Oussera', 14, 15, 23, 24, 25, 26
adjacents 'Morocco', 17, 18, 24
adjacents 'Tunisia', 2, 4, 5, 9
end
def resettle(name)
by_name(name).resettle!
end
def set_space(idx, opts, align = nil)
s = @spaces[idx]
s.alignment = align unless align.nil?
%i[gov_bases fln_bases french_troops french_police algerian_troops algerian_police
fln_underground].each { |sym| s.add(sym, opts[sym]) if opts.key? sym }
end
def short
@opposition_bases.v = 19
@support_commitment.v = 22
@commitment.v = 15
@fln_resources.v = 15
@gov_resources.v = 20
@france_track.v = 4
@border_zone_track.v = 3
@out_of_play.init({ fln_underground: 5 })
@available.init({ gov_bases: 2, french_police: 4, fln_bases: 7, fln_underground: 8 })
resettle 'Setif'
resettle 'Tlemcen'
resettle 'Bordj Bou Arreridj'
set_space 0, { algerian_police: 1, fln_underground: 1 }, :oppose
set_space 2, { french_police: 1 }
set_space 4, { algerian_police: 1, fln_underground: 1 }, :oppose
set_space 5, { french_police: 1 }
set_space 6, { french_police: 1 }, :support
set_space 7, { fln_underground: 1 }
set_space 8, { french_troops: 4, algerian_police: 1, gov_bases: 1 }
set_space 9, { french_troops: 1, algerian_police: 1, gov_bases: 1, fln_underground: 1, fln_bases: 1 }, :oppose
set_space 10, { french_police: 1, fln_underground: 1, fln_bases: 1 }, :oppose
set_space 11, { french_police: 1 }
set_space 12, { french_police: 1, fln_underground: 1, fln_bases: 1 }, :oppose
set_space 13, { french_troops: 4, algerian_troops: 1, french_police: 1 }, :support
set_space 14, { algerian_troops: 1, gov_bases: 1 }
set_space 15, { french_police: 1, algerian_police: 1, fln_underground: 1, fln_bases: 1 }, :oppose
set_space 16, { algerian_troops: 1, french_police: 1, algerian_police: 1 }, :support
set_space 17, { french_police: 1, algerian_police: 1 }
set_space 18, { french_police: 2, fln_underground: 1 }
set_space 19, { french_police: 1, gov_bases: 1 }
set_space 20, { french_police: 1 }
set_space 22, { french_police: 1 }
set_space 23, { french_police: 1 }
set_space 24, { french_police: 1 }
set_space 27, {}, :oppose
set_space 28, { fln_underground: 4, fln_bases: 2 }
set_space 29, { fln_underground: 5, fln_bases: 2 }
spaces[28].independent!
spaces[29].independent!
end
def medium
raise 'MEDIUM scenario net implemented yet'
end
def full
raise 'FULL scenario net implemented yet'
end
end
end
|