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:
292:
293:
294:
295:
296:
297:
298:
299:
300:
301:
302:
303:
304:
305:
306:
307:
308:
309:
310:
311:
312:
313:
314:
315:
316:
317:
318:
319:
320:
321:
322:
323:
324:
325:
326:
327:
328:
329:
330:
331:
332:
333:
334:
335:
336:
337:
338:
339:
340:
341:
342:
343:
344:
345:
346:
347:
348:
349:
350:
351:
352:
353:
354:
355:
356:
|
type Game(scene:IScene, input:IInput) =
let toBitmap color lines = scene.CreateBitmap(color,lines)
let toImage (bitmap:IBitmap) = bitmap.CreateContent()
let load s =
let w,h,lines = Images.nameToValue |> Seq.find (fst >> (=) s) |> snd
scene.CreateBitmap(w,h,lines).CreateContent()
let add item = scene.Contents.Add(item)
let remove item = scene.Contents.Remove(item)
let set (element:IContent) (x,y) = element.Move(x - 16 |> float, y + 16 |> float)
let maze = "
##/------------7/------------7##
##|............|!............|##
##|./__7./___7.|!./___7./__7.|##
##|o| !.| !.|!.| !.| !o|##
##|.L--J.L---J.LJ.L---J.L--J.|##
##|..........................|##
##|./__7./7./______7./7./__7.|##
##|.L--J.|!.L--7/--J.|!.L--J.|##
##|......|!....|!....|!......|##
##L____7.|L__7 |! /__J!./____J##
#######!.|/--J LJ L--7!.|#######
#######!.|! |!.|#######
#######!.|! /__==__7 |!.|#######
-------J.LJ | ! LJ.L-------
########. | **** ! .########
_______7./7 | ! /7./_______
#######!.|! L______J |!.|#######
#######!.|! |!.|#######
#######!.|! /______7 |!.|#######
##/----J.LJ L--7/--J LJ.L----7##
##|............|!............|##
##|./__7./___7.|!./___7./__7.|##
##|.L-7!.L---J.LJ.L---J.|/-J.|##
##|o..|!.......<>.......|!..o|##
##L_7.|!./7./______7./7.|!./_J##
##/-J.LJ.|!.L--7/--J.|!.LJ.L-7##
##|......|!....|!....|!......|##
##|./____JL__7.|!./__JL____7.|##
##|.L--------J.LJ.L--------J.|##
##|..........................|##
##L--------------------------J##"
let tops = [
0b00000000, 0b00000000, 0b00000000
0b00000000, 0b00000000, 0b00000000
0b00000000, 0b00000000, 0b00000000
0b00000000, 0b00000000, 0b00000000
0b00000011, 0b11111111, 0b11000000
0b00000100, 0b00000000, 0b00100000
0b00001000, 0b00000000, 0b00010000
0b00001000, 0b00000000, 0b00010000]
let mids = [
0b00001000, 0b00000000, 0b00010000
0b00001000, 0b00000000, 0b00010000
0b00001000, 0b00000000, 0b00010000
0b00001000, 0b00000000, 0b00010000
0b00001000, 0b00000000, 0b00010000
0b00001000, 0b00000000, 0b00010000
0b00001000, 0b00000000, 0b00010000
0b00001000, 0b00000000, 0b00010000]
let bots = [
0b00001000, 0b00000000, 0b00010000
0b00001000, 0b00000000, 0b00010000
0b00000100, 0b00000000, 0b00100000
0b00000011, 0b11111111, 0b11000000
0b00000000, 0b00000000, 0b00000000
0b00000000, 0b00000000, 0b00000000
0b00000000, 0b00000000, 0b00000000
0b00000000, 0b00000000, 0b00000000]
let door' = [
0b00000000
0b00000000
0b00000000
0b00000000
0b11111111
0b00000000
0b00000000
0b00000000]
let pill' = [
0b00000000
0b00000000
0b00000000
0b00011000
0b00011000
0b00000000
0b00000000
0b00000000]
let power' = [
0b00000000
0b00011000
0b00111100
0b01111110
0b01111110
0b00111100
0b00011000
0b00000000]
let fromTriple xs =
let convert = toBitmap Paint.Blue
List.foldBack (fun (l,m,r) (ls,ms,rs) -> l::ls, m::ms, r::rs) xs ([],[],[])
|> fun (l,m,r) -> convert l, convert m, convert r
let tl, top, tr = fromTriple tops
let left, blank, right = fromTriple mids
let bl, bottom, br = fromTriple bots
let door = toBitmap Paint.White door'
let pill = toBitmap Paint.Yellow pill'
let power = toBitmap Paint.Yellow power'
let toTile c =
match c with
| '=' -> door
| '_' -> top
| '|' -> left
| '!' -> right
| '/' -> tl
| '7' -> tr
| 'L' -> bl
| 'J' -> br
| '-' -> bottom
| '.' -> pill
| 'o' -> power
| _ -> blank
let isWall = function
| '_' | '|' | '!' | '/' | '7' | 'L' | 'J' | '-' | '*' -> true
| _ -> false
let walls = scene.AddLayer()
let lines = maze.Split('\n')
let tiles =
lines |> Array.mapi (fun y line ->
line.ToCharArray() |> Array.mapi (fun x item ->
let tile = toTile item |> toImage
set tile (x * 8, y * 8)
if isWall item
then walls.Contents.Add tile |> ignore
else scene.Contents.Add tile |> ignore
tile
)
)
let route_home =
let numbers =
lines |> Array.map (fun line ->
line.ToCharArray()
|> Array.map (fun c -> if isWall c then System.Int32.MaxValue else -1)
)
let canFill (x,y) =
y>=0 && y < numbers.Length &&
x>=0 && x < numbers.[y].Length &&
numbers.[y].[x] = -1
let fill n (x,y) = numbers.[y].[x] <- n
flood canFill fill (16,16)
numbers
let tileAt x y =
if x < 0 || x > 30 then ' '
else lines.[y].[x]
let isWallAt (x,y) = tileAt x y |> isWall
let p = load "p"
let pu = load "pu1",load "pu2"
let pd = load "pd1",load "pd2"
let pl = load "pl1",load "pl2"
let pr = load "pr1",load "pr2"
let lives = [for _ in 1..2 -> load "pl1"]
do lives |> List.iteri (fun i life -> add life; set life (16+16*i,32*8))
let ghost_starts =
[
"red", (16, 12), (1,0)
"cyan", (14, 16), (1,0)
"pink" , (16, 14), (0,-1)
"orange" , (18, 16), (-1,0)
]
|> List.map (fun (color,(x,y),v) ->
let blue = load "blue"
let eyes = load "eyeu", load "eyed", load "eyel", load "eyer"
let body = load (color+"u"), load (color+"d"), load (color+"l"), load (color+"r")
let _,image,_,_ = body
{ Blue=blue; Eyes=eyes; Body=body; X=x*8-7; Y=y*8-3; V=v; Image=image; IsReturning=false }
)
let mutable ghosts = ghost_starts
do ghosts |> List.iter (fun ghost ->
add ghost.Image
set ghost.Image (ghost.X,ghost.Y)
)
let pacman = ref p
do add !pacman
let mutable powerCount = 0
let x = ref (16 * 8 - 7)
let y = ref (24 * 8 - 3)
let v = ref (0,0)
let noWall (x,y) (ex,ey) =
let bx, by = int ((x+6+ex)/8), int ((y+6+ey)/8)
isWallAt (bx,by) |> not
let fillValue (x,y) (ex,ey) =
let bx, by = int ((x+6+ex)/8), int ((y+6+ey)/8)
route_home.[by].[bx]
let verticallyAligned (x,y) = x % 8 = 5
let horizontallyAligned (x,y) = y % 8 = 5
let canGoUp (x,y) = verticallyAligned (x,y) && noWall (x,y) (0,-4)
let canGoDown (x,y) = verticallyAligned (x,y) && noWall (x,y) (0,5)
let canGoLeft (x,y) = horizontallyAligned (x,y) && noWall (x,y) (-4,0)
let canGoRight (x,y) = horizontallyAligned (x,y) && noWall (x,y) (5,0)
let fillUp (x,y) = fillValue (x,y) (0,-4)
let fillDown (x,y) = fillValue (x,y) (0,5)
let fillLeft (x,y) = fillValue (x,y) (-4,0)
let fillRight (x,y) = fillValue (x,y) (5,0)
let go (x,y) (dx,dy) =
let x =
if dx = -1 && x = 0 then 30 * 8
elif dx = 1 && x = 30 *8 then 0
else x
x + dx, y + dy
let newGhosts () =
ghosts |> List.map (fun ghost ->
let x, y = ghost.X, ghost.Y
let dx, dy = ghost.V
let u,d,l,r = ghost.Body
let u',d',l',r' = ghost.Eyes
let face, eye, canMove =
match dx, dy with
| 0,-1 -> u, u', canGoUp (x,y)
| 0, 1 -> d, d', canGoDown (x,y)
| -1,0 -> l, l', canGoLeft (x,y)
| 1, 0 -> r, r', canGoRight (x,y)
| _, _ -> invalidOp ""
let isBackwards (a,b) =
(a <> 0 && a = -dx) || (b <> 0 && b = -dy)
let directions =
[
if canGoUp (x,y) then yield (0,-1), fillUp (x,y)
if canGoDown (x,y) then yield (0,1), fillDown (x,y)
if canGoLeft (x,y) then yield (-1,0), fillLeft (x,y)
if canGoRight(x,y) then yield (1,0), fillRight (x,y)
]
let directions =
if ghost.IsReturning then
directions
|> Seq.toArray
|> Array.sortBy snd
|> Seq.map fst
|> Seq.toArray
//|> Array.sortBy isBackwards
//|> Seq.toArray
else
directions
|> Seq.map fst
|> Seq.toArray
|> Seq.unsort
|> Seq.toArray
|> Array.sortBy isBackwards
|> Seq.toArray
let dx, dy =
let newDirection =
directions |> Seq.head
if not <| isBackwards newDirection
then newDirection
else dx,dy
let x,y = go (x,y) (dx,dy)
let returning =
if ghost.IsReturning && 0 = (fillValue (x,y) (0,0))
then false
else ghost.IsReturning
remove ghost.Image
let face =
if ghost.IsReturning then eye
else
if powerCount > 0 then ghost.Blue else face
add face
set face (x,y)
{ ghost with X = x; Y = y; V = (dx,dy); Image = face; IsReturning = returning }
)
let updateGhosts () = ghosts <- newGhosts ()
let updatePacman () =
let directions =
[
if input.IsUp && canGoUp (!x,!y) then yield (0,-1), pu
if input.IsDown && canGoDown (!x,!y) then yield (0,1), pd
if input.IsLeft && canGoLeft (!x,!y) then yield (-1,0), pl
if input.IsRight && canGoRight (!x,!y) then yield (1,0), pr
]
|> List.sortBy (fun (v',_) -> v' = !v)
let move ((dx,dy),(d1,d2)) =
let x', y' = go (!x,!y) (dx,dy)
x := x'; y := y'; v := (dx,dy)
remove !pacman
let d = if (!x/6 + !y/6) % 2 = 0 then d1 else d2
add d
pacman := d
if directions.Length > 0 then
directions.Head |> move
let tx, ty = int ((!x+6)/8), int ((!y+6)/8)
if tileAt tx ty = '.' then
remove (tiles.[ty].[tx])
if tileAt tx ty = 'o' then
if scene.Contents.Contains (tiles.[ty].[tx]) then
powerCount <- 500
remove (tiles.[ty].[tx])
set !pacman (!x,!y)
let updatePower () =
if powerCount > 0 then
if (powerCount/5) % 2 = 1 then walls.SetOpacity(0.5)
else walls.SetOpacity(1.0)
else walls.SetOpacity(1.0)
powerCount <- powerCount - 1
let mutable flashCount = 0
let updateFlash () =
if flashCount > 0 then
if ((flashCount / 5) % 2) = 1 then (!pacman).SetOpacity(0.5)
else (!pacman).SetOpacity(1.0)
else (!pacman).SetOpacity(1.0)
flashCount <- flashCount - 1
let touchGhosts () =
let px, py = !x, !y
ghosts |> List.filter (fun ghost ->
let x,y = ghost.X, ghost.Y
((px >= x && px < x + 13) ||
(x < px + 13 && x >= px)) &&
((py >= y && py < y + 13) ||
(y < py + 13 && y >= py))
)
let update () =
updatePacman ()
updateGhosts ()
let touching = touchGhosts()
if touching.Length > 0 then
if powerCount > 0
then ghosts <- ghosts |> List.mapi (fun i ghost ->
if touching |> List.exists ((=) ghost)
then
let ghost' = ghost_starts.[i]
{ ghost with IsReturning = true; }
else ghost
)
else flashCount <- 20
updateFlash ()
updatePower ()
member this.Update () = update ()
|