a097ff554658301a760f781a3f47333344e67426
1 # Monsterz - Chains of Friends
3 # 2010-2014 (c) Jean Privat <jean@pryen.org>
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the Do What The Fuck You Want To
7 # Public License, Version 2, as published by Sam Hocevar. See
8 # http://sam.zoy.org/projects/COPYING.WTFPL for more details.
10 # Game login on the grid of monsters
15 # width of the current grid
16 var width
: Int is noinit
18 # maximum width of the grid
21 # height of the current grid
22 var height
: Int is noinit
24 # maximum height of the grid
27 # nm is the number of monster + 1 for the empty tile
31 private var grid
= new Array[Array[Tile]]
35 # Reinitialize the grid with new empty tiles and monsters info
41 for i
in [0..max_width
[ do
42 self.grid
[i
] = new Array[Tile]
43 for j
in [0..max_height
[ do
44 var t
= new Tile(self, i
, j
)
48 self.monsters
= new Array[MonsterInfo]
49 for i
in [0..nb_monsters
] do
50 self.monsters
[i
] = new MonsterInfo
52 self.resize
(max_width
,max_height
)
56 # if `fixed` is false, fixed monsters remains
57 fun reset
(fixed
: Bool): Bool
60 for i
in [0..width
[ do
61 for j
in [0..height
[ do
62 var t
= self.grid
[i
][j
]
63 if fixed
then t
.fixed
= false
64 if not t
.fixed
and t
.kind
>0 then
73 # Total number of monsters in the grid
76 # Number of monsters alone or with >=3 next
79 # information about each kind of monsters
80 var monsters
= new Array[MonsterInfo]
85 # Check that the monster of `kind` form a complete chain.
86 # if not null, `t` is used as a starting tile to check the chain
87 fun check_chain
(kind
: Int, t
: nullable Tile): Bool
89 var m
= monsters
[kind
]
90 if m
.angry
> 0 or m
.lonely
> 0 or m
.single
> 2 then
91 # easy case for no chain
92 # print "easy {kind} chain: false"
97 # Search for a starting
98 for x
in [0..width
[ do for y
in [0..height
[ do
100 if t2
== null or t2
.kind
!= kind
then continue
109 # print "get neighbor {t}"
111 assert t
.kind
== kind
112 var c
= count_chain
(t
, 1000.rand
)
113 # print "old {kind} chain? {c} / {m.number}"
114 m
.chain
= c
== m
.number
119 # The total number of monsters connected to the chain of `t`
120 # `mark` is a uniq number used to mark tiles
121 fun count_chain
(t
: Tile, mark
: Int): Int
127 if (i
==0 and j
==0) or (i
!=0 and j
!=0) then continue
128 var t2
= get
(t
.x
+i
,t
.y
+j
)
129 if t2
== null then continue
130 if t2
.chain_mark
== mark
or t2
.kind
!= t
.kind
then continue
131 res
+= count_chain
(t2
, mark
)
137 # Resize the grid. Do not touch the content.
144 # Try to get the tila at `x`,`y`.
145 # Returns null if the position is out of bound.
146 fun get
(x
,y
: Int): nullable Tile
148 if x
<0 or x
>=self.width
or y
<0 or y
>=self.height
then return null
149 return self.grid
[x
][y
]
153 var fixed_shaped
= """[
154 [{x:1,y:0},{x:2,y:0},{x:1,y:1},{x:2,y:1}],
155 [{x:0,y:0},{x:0,y:1},{x:0,y:2}],
156 [{x:1,y:2},{x:2,y:2},{x:3,y:2}],
157 [{x:4,y:1},{x:4,y:2}],
158 [{x:3,y:0},{x:4,y:0}],
162 # Set shapes for the fixed blocks.
165 for i
in [0..width
[ do
166 for j
in [0..height
[ do
167 var t
= self.grid
[i
][j
]
168 if t
.fixed
then t
.shape
= null
171 for shape
in fixed_shaped
.split
(",") do
172 for i
in [0..width
[ do
173 for j
in [0..height
[ do
174 var ts
= new Array[Tile]
175 for l
in [0..shape
.length
[ do
176 #var t = self.get(i+shape[l].x-shape[0].x,j+shape[l].y-shape[0].y)
177 var t
= self.get
(i
,j
)
178 if t
!= null and t
.fixed
and t
.shape
== null then ts
.push
(t
)
180 if ts
.length
== shape
.length
then
181 for l
in [0..shape
.length
[ do
182 ts
[l
].shape
= shape
[l
]
190 # Return the serialization of the fixed tiles. */
194 var str
= ".#ABCDEFGHI"
195 for y
in [0..height
[ do
197 var last
: nullable Int = null
198 for x
in [0..width
[ do
199 var t
= self.grid
[x
][y
]
201 if t
.fixed
then tk
= t
.kind
+ 1
202 if tk
== last
and rle
<9 then
206 if rle
>1 then res
+= rle
.to_s
207 res
+= str
.chars
[last
].to_s
214 if rle
>1 then res
+= rle
.to_s
215 res
+= str
.chars
[last
].to_s
222 # Load a new grid from a seialization.
223 fun load
(str
: String): Bool
239 if x
> mx
then mx
= x
242 else if c
== '.' then
244 else if c
== '#' then
245 var t
= self.get
(x
,y
)
248 else if c
>= 'A' and c
<= 'I' then
249 var t
= self.get
(x
,y
)
251 t
.update
(c
.ascii-
'A'.ascii
+1)
254 else if c
>= '1' and c
<= '9' then
262 if x
> mx
then mx
= x
263 if y
> my
then my
= y
264 if mx
<3 or my
<3 or mx
>=max_width
or my
>=max_height
then
272 # A ASCII version of the grid.
273 redef fun to_s
: String
275 var ansicols
= once
["37;1","31","36;1","32;1","35;1","33;1","33","34;1","31;1","37"]
276 var b
= new FlatBuffer
277 b
.append
("{width}x{height}\n")
278 for j
in [0..height
[ do
279 for i
in [0..width
[ do
284 if t
.fixed
then c
= '#'
289 c
= (k
+ 'a'.ascii
- 1).ascii
290 if t
.fixed
then c
= c
.to_upper
305 # Return a copy of the current grid.
306 # if (!no_fixed) copy only the fixed tiles.
307 fun copy
(no_fixed
: Bool): Grid
309 var g
= new Grid(self.max_width
, self.max_height
, self.nb_monsters
)
310 g
.resize
(width
, height
)
311 for y
in [0..height
[ do
312 for x
in [0..width
[ do
313 var t
= self.grid
[x
][y
]
314 if no_fixed
or t
.fixed
then
315 var t2
= g
.grid
[x
][y
]
325 # Internal check of the validity of tile and monster informations
328 var m2
= new Array[MonsterInfo]
329 for m
in [0..nb_monsters
] do
330 m2
[m
] = new MonsterInfo
332 for x
in [0..width
[ do
333 for y
in [0..height
[ do
341 if k
== 0 then continue
345 if i
== j
or (i
!= 0 and j
!= 0) then continue
346 var t2
= get
(x
+i
, y
+j
)
347 if t2
== null then continue
350 else if t2
.kind
== 0 and not t2
.fixed
then
355 assert n
== t
.nexts
else
357 print
"{t} says {t.nexts} nexts, found {n}"
359 #assert f == t.frees else
372 for m
in [1..nb_monsters
] do
373 assert m2
[m
].number
== monsters
[m
].number
374 assert m2
[m
].lonely
== monsters
[m
].lonely
375 assert m2
[m
].single
== monsters
[m
].single
376 assert m2
[m
].angry
== monsters
[m
].angry
381 # Information about each kind of monsters
383 # number of monsters of this kind on board
385 # number of monsters of this kind to place, -1 if no limit
386 var remains
: Int = -1
387 # number of monsters that have exactly 1 next
389 # number of monsters that have exactly 0 next
391 # number of monsters that have 3 or more next
393 # Are all monsters form a wining chain?
397 # A localized tile of a grid, can contain a monster and be fixed.
399 # The grid of the tile.
402 # The x coordinate in the grid (starting from 0).
405 # The y coordinate in the grid (starting from 0).
408 # The kind of monster in the grid. 0 means empty.
411 # blink time to live (0 means no blinking).
414 # shocked time to live (0 means not shocked)
417 # number of neighbors of the same kind.
420 # number of free non fixed next tiles
423 # is the tile editable (metal block)
434 return "\{{x},{y}:{s.chars[kind]}\}"
437 # Shape for metal block
438 var shape
: nullable Object = null
440 # Flag for `count_chain` computation.
441 private var chain_mark
= 0
443 # Set a new kind of monster on tile
444 # Return true is the move made the grid unsolvable (bad move)
445 fun update
(nkind
: Int): Bool
451 if okind
== nkind
then return false
454 # First, remove it and update info.
456 var m
= g
.monsters
[okind
]
474 var a_neigbor
: nullable Tile = null
478 if (i
==0 and j
==0) or (i
!=0 and j
!=0) then continue
479 var t2
= g
.get
(t
.x
+i
,t
.y
+j
)
480 if t2
== null then continue
482 if not t2
.fixed
then t
.frees
+= 1
485 var m
= g
.monsters
[t2
.kind
]
487 if t2
.kind
== okind
then
488 if a_neigbor
== null then a_neigbor
= t2
489 # same than old, thus dec neighbors
502 # print "+ {t} one less next: {t2} ; +({i}x{j})"
505 if t2
.kind
== nkind
then
506 # Same than new, thus inc neighbors
523 # print "+ {t} one more next: {t2}"
528 # Add and update neighbors info
531 var m
= g
.monsters
[nkind
]
546 g
.check_chain
(nkind
, t
)
549 # check if the old kind broke, or create a chain
551 g
.check_chain
(okind
, a_neigbor
)
556 for m
in g
.monsters
do
557 if m
.number
> 0 and not m
.chain
then g
.won
= false