3913154f1370912d7755cb1583da2e5b9ba555db
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
18 # maximum width of the grid
21 # height of the current grid
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]]
41 # Reinitialize the grid with new empty tiles and monsters info
47 for i
in [0..max_width
[ do
48 self.grid
[i
] = new Array[Tile]
49 for j
in [0..max_height
[ do
50 var t
= new Tile(self, i
, j
)
54 self.monsters
= new Array[MonsterInfo]
55 for i
in [0..nb_monsters
] do
56 self.monsters
[i
] = new MonsterInfo
58 self.resize
(max_width
,max_height
)
62 # if `fixed` is false, fixed monsters remains
63 fun reset
(fixed
: Bool): Bool
66 for i
in [0..width
[ do
67 for j
in [0..height
[ do
68 var t
= self.grid
[i
][j
]
69 if fixed
then t
.fixed
= false
70 if not t
.fixed
and t
.kind
>0 then
79 # Total number of monsters in the grid
82 # Number of monsters alone or with >=3 next
85 # information about each kind of monsters
86 var monsters
= new Array[MonsterInfo]
91 # Check that the monster of `kind` form a complete chain.
92 # if not null, `t` is used as a starting tile to check the chain
93 fun check_chain
(kind
: Int, t
: nullable Tile): Bool
95 var m
= monsters
[kind
]
96 if m
.angry
> 0 or m
.lonely
> 0 or m
.single
> 2 then
97 # easy case for no chain
98 # print "easy {kind} chain: false"
103 # Search for a starting
104 for x
in [0..width
[ do for y
in [0..height
[ do
106 if t2
== null or t2
.kind
!= kind
then continue
115 # print "get neighbor {t}"
117 assert t
.kind
== kind
118 var c
= count_chain
(t
, 1000.rand
)
119 # print "old {kind} chain? {c} / {m.number}"
120 m
.chain
= c
== m
.number
125 # The total number of monsters connected to the chain of `t`
126 # `mark` is a uniq number used to mark tiles
127 fun count_chain
(t
: Tile, mark
: Int): Int
133 if (i
==0 and j
==0) or (i
!=0 and j
!=0) then continue
134 var t2
= get
(t
.x
+i
,t
.y
+j
)
135 if t2
== null then continue
136 if t2
.chain_mark
== mark
or t2
.kind
!= t
.kind
then continue
137 res
+= count_chain
(t2
, mark
)
143 # Resize the grid. Do not touch the content.
150 # Try to get the tila at `x`,`y`.
151 # Returns null if the position is out of bound.
152 fun get
(x
,y
: Int): nullable Tile
154 if x
<0 or x
>=self.width
or y
<0 or y
>=self.height
then return null
155 return self.grid
[x
][y
]
159 var fixed_shaped
= """[
160 [{x:1,y:0},{x:2,y:0},{x:1,y:1},{x:2,y:1}],
161 [{x:0,y:0},{x:0,y:1},{x:0,y:2}],
162 [{x:1,y:2},{x:2,y:2},{x:3,y:2}],
163 [{x:4,y:1},{x:4,y:2}],
164 [{x:3,y:0},{x:4,y:0}],
168 # Set shapes for the fixed blocks.
171 for i
in [0..width
[ do
172 for j
in [0..height
[ do
173 var t
= self.grid
[i
][j
]
174 if t
.fixed
then t
.shape
= null
177 for shape
in fixed_shaped
.split
(",") do
178 for i
in [0..width
[ do
179 for j
in [0..height
[ do
180 var ts
= new Array[Tile]
181 for l
in [0..shape
.length
[ do
182 #var t = self.get(i+shape[l].x-shape[0].x,j+shape[l].y-shape[0].y)
183 var t
= self.get
(i
,j
)
184 if t
!= null and t
.fixed
and t
.shape
== null then ts
.push
(t
)
186 if ts
.length
== shape
.length
then
187 for l
in [0..shape
.length
[ do
188 ts
[l
].shape
= shape
[l
]
196 # Return the serialization of the fixed tiles. */
200 var str
= ".#ABCDEFGHI"
201 for y
in [0..height
[ do
203 var last
: nullable Int = null
204 for x
in [0..width
[ do
205 var t
= self.grid
[x
][y
]
207 if t
.fixed
then tk
= t
.kind
+ 1
208 if tk
== last
and rle
<9 then
212 if rle
>1 then res
+= rle
.to_s
213 res
+= str
.chars
[last
].to_s
220 if rle
>1 then res
+= rle
.to_s
221 res
+= str
.chars
[last
].to_s
228 # Load a new grid from a seialization.
229 fun load
(str
: String): Bool
245 if x
> mx
then mx
= x
248 else if c
== '.' then
250 else if c
== '#' then
251 var t
= self.get
(x
,y
)
254 else if c
>= 'A' and c
<= 'I' then
255 var t
= self.get
(x
,y
)
257 t
.update
(c
.ascii-
'A'.ascii
+1)
260 else if c
>= '1' and c
<= '9' then
268 if x
> mx
then mx
= x
269 if y
> my
then my
= y
270 if mx
<3 or my
<3 or mx
>=max_width
or my
>=max_height
then
278 # A ASCII version of the grid.
279 redef fun to_s
: String
281 var ansicols
= once
["37;1","31","36;1","32;1","35;1","33;1","33","34;1","31;1","37"]
282 var b
= new FlatBuffer
283 b
.append
("{width}x{height}\n")
284 for j
in [0..height
[ do
285 for i
in [0..width
[ do
290 if t
.fixed
then c
= '#'
295 c
= (k
+ 'a'.ascii
- 1).ascii
296 if t
.fixed
then c
= c
.to_upper
311 # Return a copy of the current grid.
312 # if (!no_fixed) copy only the fixed tiles.
313 fun copy
(no_fixed
: Bool): Grid
315 var g
= new Grid(self.max_width
, self.max_height
, self.nb_monsters
)
316 g
.resize
(width
, height
)
317 for y
in [0..height
[ do
318 for x
in [0..width
[ do
319 var t
= self.grid
[x
][y
]
320 if no_fixed
or t
.fixed
then
321 var t2
= g
.grid
[x
][y
]
331 # Internal check of the validity of tile and monster informations
334 var m2
= new Array[MonsterInfo]
335 for m
in [0..nb_monsters
] do
336 m2
[m
] = new MonsterInfo
338 for x
in [0..width
[ do
339 for y
in [0..height
[ do
347 if k
== 0 then continue
351 if i
== j
or (i
!= 0 and j
!= 0) then continue
352 var t2
= get
(x
+i
, y
+j
)
353 if t2
== null then continue
356 else if t2
.kind
== 0 and not t2
.fixed
then
361 assert n
== t
.nexts
else
363 print
"{t} says {t.nexts} nexts, found {n}"
365 #assert f == t.frees else
378 for m
in [1..nb_monsters
] do
379 assert m2
[m
].number
== monsters
[m
].number
380 assert m2
[m
].lonely
== monsters
[m
].lonely
381 assert m2
[m
].single
== monsters
[m
].single
382 assert m2
[m
].angry
== monsters
[m
].angry
387 # Information about each kind of monsters
389 # number of monsters of this kind on board
391 # number of monsters of this kind to place, -1 if no limit
392 var remains
: Int = -1
393 # number of monsters that have exactly 1 next
395 # number of monsters that have exactly 0 next
397 # number of monsters that have 3 or more next
399 # Are all monsters form a wining chain?
403 # A localized tile of a grid, can contain a monster and be fixed.
405 # The grid of the tile.
408 # The x coordinate in the grid (starting from 0).
411 # The y coordinate in the grid (starting from 0).
414 # The kind of monster in the grid. 0 means empty.
417 # blink time to live (0 means no blinking).
420 # shocked time to live (0 means not shocked)
423 # number of neighbors of the same kind.
426 # number of free non fixed next tiles
429 # is the tile editable (metal block)
440 return "\{{x},{y}:{s.chars[kind]}\}"
443 # Shape for metal block
444 var shape
: nullable Object = null
446 # Flag for `count_chain` computation.
447 private var chain_mark
= 0
449 # Set a new kind of monster on tile
450 # Return true is the move made the grid unsolvable (bad move)
451 fun update
(nkind
: Int): Bool
457 if okind
== nkind
then return false
460 # First, remove it and update info.
462 var m
= g
.monsters
[okind
]
480 var a_neigbor
: nullable Tile = null
484 if (i
==0 and j
==0) or (i
!=0 and j
!=0) then continue
485 var t2
= g
.get
(t
.x
+i
,t
.y
+j
)
486 if t2
== null then continue
488 if not t2
.fixed
then t
.frees
+= 1
491 var m
= g
.monsters
[t2
.kind
]
493 if t2
.kind
== okind
then
494 if a_neigbor
== null then a_neigbor
= t2
495 # same than old, thus dec neighbors
508 # print "+ {t} one less next: {t2} ; +({i}x{j})"
511 if t2
.kind
== nkind
then
512 # Same than new, thus inc neighbors
529 # print "+ {t} one more next: {t2}"
534 # Add and update neighbors info
537 var m
= g
.monsters
[nkind
]
552 g
.check_chain
(nkind
, t
)
555 # check if the old kind broke, or create a chain
557 g
.check_chain
(okind
, a_neigbor
)
562 for m
in g
.monsters
do
563 if m
.number
> 0 and not m
.chain
then g
.won
= false