This is a full blown Pacman game. If you're looking for an introduction to Fable
then visit other tutorials, in particular the Mario game which
is much simpler.
Some of the graphics, maze structure and walls are defined as embedded strings or
arrays in the following section, so that the game is stand-alone and easily portable.
The following block embeds the ghosts and other parts of graphics as Base64 encoded strings.
This way, we can load them without making additional server requests:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
|
module Images =
let cyand = "data:image/png;base64,iVBOR..."
let oranged = "data:image/png;base64,iVBOR..."
(Other images omitted)
// Create image using the specified data
let createImage data =
let img = document.createElement_img()
img.src <- data
img
|
The second part defines the maze, tile bits and blank block. The maze is defined as one big string
using ASCII-art encoding. Where /7LJ
represent corners (upper-left, upper-right, lower-left and lower-right),
!|-_
represent walls (left, right, top, bottom)and
o.` represent two kinds of pills in the maze.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
|
// Define the structure of the maze using ASCII
let maze =
"##|./__7./___7.|!./___7./__7.|##,"...
let tileBits =
[| [|0b00000000;0b00000000;0b00000000;
0b00000000;0b00000011;0b00000100;
0b00001000;0b00001000|]
[|0b00000000;0b00000000;... |]
let blank =
[| 0b00000000;0b00000000;0b00000000;
0b00000000;0b00000000; ...|]
|
The following functions parse the maze representation and check various properties of the maze.
Those are used for rendering, but also for checking whether Pacman can go in a given direction.
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:
|
/// Characters _|!/7LJ represent different walls
let isWall (c:char) =
"_|!/7LJ-".IndexOf(c) <> -1
/// Returns ' ' for positions outside of range
let tileAt (x,y) =
if x < 0 || x > 30 then ' ' else maze.[y].[x]
/// Is the maze tile at x,y a wall?
let isWallAt (x,y) =
tileAt(x,y) |> isWall
// Is Pacman at a point where it can turn?
let verticallyAligned (x,y) = (x % 8) = 5
let horizontallyAligned (x,y) = (y % 8) = 5
let isAligned n = (n % 8) = 5
// Check whether Pacman can go in given direction
let noWall (x,y) (ex,ey) =
let bx, by = (x+6+ex) >>> 3, (y+6+ey) >>> 3
isWallAt (bx,by) |> not
let canGoUp (x,y) = isAligned x && noWall (x,y) (0,-4)
let canGoDown (x,y) = isAligned x && noWall (x,y) (0,5)
let canGoLeft (x,y) = isAligned y && noWall (x,y) (-4,0)
let canGoRight (x,y) = isAligned y && noWall (x,y) (5,0)
|
To render the background, we first fill the background
and then iterate over the string lines that represent the maze and we draw images of
walls specified in the tileBits
value earlier (or use blank
tile for all other characters).
The following is used to map from tile characters to the tileBits
values and to draw individual lines:
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:
|
// Mapping from Maze walls to tileBits
let tileColors = "BBBBBBBBBYY"
let tileChars = "/_7|!L-J=.o"
/// Returns tile for a given Maze character
let toTile (c:char) =
let i = tileChars.IndexOf(c)
if i = -1 then blank, 'B'
else tileBits.[i], tileColors.[i]
/// Draw the lines specified by a wall tile
let draw f (lines:int[]) =
let width = 8
lines |> Array.iteri (fun y line ->
for x = 0 to width-1 do
let bit = (1 <<< (width - 1 - x))
let pattern = line &&& bit
if pattern <> 0 then f (x,y) )
/// Creates a brush for rendering the given RGBA color
let createBrush (context:CanvasRenderingContext2D) (r,g,b,a) =
let id = context.createImageData(U2.Case1 1.0, 1.0)
let d = id.data
d.[0] <- float r; d.[1] <- float g
d.[2] <- float b; d.[3] <- float a
id
|
The main function for rendering background just fills the canvas with a black color and
then iterates over the Maze tiles and renders individual walls:
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:
|
let createBackground () =
// Fill background with black
let background = document.createElement_canvas()
background.width <- 256.
background.height <- 256.
let context = background.getContext_2d()
context.fillStyle <- U3.Case1 "rgb(0,0,0)"
context.fillRect (0., 0. , 256., 256.);
// Render individual tiles of the maze
let blue = createBrush context (63,63,255,255)
let yellow = createBrush context (255,255,0,255)
let lines = maze
for y = 0 to lines.Length-1 do
let line = lines.[y]
for x = 0 to line.Length-1 do
let c = line.[x]
let tile, color = toTile c
let brush = match color with 'Y' -> yellow | _ -> blue
let f (x',y') =
context.putImageData
(brush, float (x*8 + x'), float (y*8 + y'))
draw f tile
background
/// Clear whatever is rendered in the specified Maze cell
let clearCell (background : HTMLCanvasElement) (x,y) =
let context = background.getContext_2d()
context.fillStyle <- U3.Case1 "rgb(0,0,0)"
context.fillRect (float (x*8), float (y*8), 8., 8.);
|
Ghosts are represented by a simple F# class type that contains the image of the ghost,
current X, Y positions and a velocity in both directions. In Pacman, ghosts are mutable
and expose Move
and Reset
methods that change their properties.
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:
|
/// Wrap around the sides of the Maze
let wrap (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
/// Mutable representation of a ghost
type Ghost(image:HTMLImageElement,x,y,v) =
let mutable x' = x
let mutable y' = y
let mutable v' = v
member val Image = image
member val IsReturning = false with get, set
member __.X = x'
member __.Y = y'
member __.V = v'
/// Move back to initial location
member ghost.Reset() =
x' <- x
y' <- y
/// Move in the current direction
member ghost.Move(v) =
v' <- v
let dx,dy = v
let x,y = wrap (x',y') (dx,dy)
x' <- x
y' <- y
|
At the beginning, we have red, cyan, pink and orange ghosts
in the middle of the maze:
1:
2:
3:
4:
5:
6:
7:
|
let createGhosts context =
[| Images.redd, (16, 11), (1,0)
Images.cyand, (14, 15), (1,0)
Images.pinkd, (16, 13), (0,-1)
Images.oranged, (18, 15), (-1,0) |]
|> Array.map (fun (data,(x,y),v) ->
Ghost(createImage data, (x*8)-7, (y*8)-3, v) )
|
For generating Ghost movements, we need an implementation of the Flood fill algorithm,
which we use to generate the shortest path home when Ghosts are returning. The fillValue
function does this, by starting
at a specified location (which can be one of the directions in which ghosts can go).
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:
|
/// Recursive flood fill function
let flood canFill fill (x,y) =
let rec f n = function
| [] -> ()
| ps ->
let ps = ps |> List.filter (fun (x,y) -> canFill (x,y))
ps |> List.iter (fun (x,y) -> fill (x,y,n))
ps |> List.collect (fun (x,y) ->
[(x-1,y);(x+1,y);(x,y-1);(x,y+1)]) |> f (n+1)
f 0 [(x,y)]
/// Possible routes that take the ghost home
let route_home =
let numbers =
maze |> Array.map (fun line ->
line.ToCharArray()
|> Array.map (fun c -> if isWall c then 999 else -1) )
let canFill (x:int,y:int) =
y>=0 && y < (numbers.Length-1) &&
x>=0 && x < (numbers.[y].Length-1) &&
numbers.[y].[x] = -1
let fill (x,y,n) = numbers.[y].[x] <- n
flood canFill fill (16,15)
numbers
/// Find the shortest way home from specified location
/// (adjusted by offset in which ghosts start)
let fillValue (x,y) (ex,ey) =
let bx = int (floor(float ((x+6+ex)/8)))
let by = int (floor(float ((y+6+ey)/8)))
route_home.[by].[bx]
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)
|
When choosing a direction, ghosts that are returning will go in the direction
that leads them home. Other ghosts generate a list of possible directions (the directions
array)
and then filter those that are in the direction of Pacman and choose one of the options. If they
are stuck and cannot go in any way, they stay where they are.
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:
|
let chooseDirection (ghost:Ghost) =
let x,y = ghost.X, ghost.Y
let dx,dy = ghost.V
// Are we facing towards the given point?
let isBackwards (a,b) =
(a <> 0 && a = -dx) || (b <> 0 && b = -dy)
// Generate array with possible directions
let directions =
[|if canGoLeft(x,y) then yield (-1,0), fillLeft(x,y)
if canGoDown(x,y) then yield (0,1), fillDown(x,y)
if canGoRight(x,y) then yield (1,0), fillRight(x,y)
if canGoUp(x,y) then yield (0,-1), fillUp(x,y) |]
if ghost.IsReturning then
// Returning ghosts find the shortest way home
let xs = directions |> Array.sortBy snd
let v, n = xs.[0]
if n = 0 then ghost.IsReturning <- false
v
else
// Other ghosts pick one direction twoards Pacman
let xs =
directions
|> Array.map fst
|> Array.filter (not << isBackwards)
if xs.Length = 0 then 0, 0
else
let i = random() * float xs.Length
xs.[int (floor i)]
|
The Keyboard
module records the keys that are currently pressed in a set and
registers window
event handlers using the DOM interoperability layer.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
|
module Keyboard =
/// Set of currently pressed keys
let mutable keysPressed = Set.empty
/// Update the keys as requested
let reset () = keysPressed <- Set.empty
let isPressed keyCode = Set.contains keyCode keysPressed
/// Triggered when key is pressed/released
let update (e : KeyboardEvent, pressed) =
let keyCode = int e.keyCode
let op = if pressed then Set.add else Set.remove
keysPressed <- op keyCode keysPressed
null
/// Register DOM event handlers
let init () =
window.addEventListener_keydown(fun e -> update(e, true))
window.addEventListener_keyup(fun e -> update(e, false))
|
The Pacman
module is a helper that exposes the imageAt
function. The function
returns the pacman image for the specified X and Y location, taking into account the
direction in which Pacman is going. It keeps a mutable state with current step of Pacman's
mouth.
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:
|
module Pacman =
// Load the different Pacman images
let private pu1, pu2 =
createImage Images.pu1, createImage Images.pu2
let private pd1, pd2 =
createImage Images.pd1, createImage Images.pd2
let private pl1, pl2 =
createImage Images.pl1, createImage Images.pl2
let private pr1, pr2 =
createImage Images.pr1, createImage Images.pr2
// Represent Pacman's mouth state
let private lastp = ref pr1
// Return imge for the given X, Y and V
let imageAt(x,y,v) =
let p1, p2 =
match !v with
| -1, 0 -> pl1, pl2
| 1, 0 -> pr1, pr2
| 0, -1 -> pu1, pu2
| 0, 1 -> pd1, pd2
| _, _ -> !lastp, !lastp
let x' = int (floor(float (!x/6)))
let y' = int (floor(float (!y/6)))
let p = if (x' + y') % 2 = 0 then p1 else p2
lastp := p
p
|
We need two more helpers - the sound
function plays an audio file. This uses JavaScript native
Audio
object, which we access via the Emit
attribute. The other helper is countDots
which
counts dots in the maze:
1:
2:
3:
4:
5:
6:
7:
8:
9:
|
/// Uses Fable's Emit to call JavaScript directly
[<Emit("(new Audio($0)).play();")>]
let sound(file:string) : unit = failwith "never"
/// Count number of dots in the maze
let countDots () =
maze |> Array.sumBy (fun line ->
line.ToCharArray()
|> Array.sumBy (function '.' -> 1 | 'o' -> 1 | _ -> 0))
|
Most of the Pacman game logic is wrapped in the top-level playLevel
function. This takes two functions - that are called
when the game completes - and then it iniializes the world state and runs in a loop until the end of the game.
The following outlines the structure of the function:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
|
let playLevel (onLevelCompleted, onGameOver) =
// (Create canvas, background and ghosts)
// (Define the Pacman state)
// (Move ghosts and Pacman)
// (Detect pills and collisiions)
// (Rendering everything in the game)
let rec update () =
logic ()
render ()
if !dotsLeft = 0 then onLevelCompleted()
elif !energy <= 0 then onGameOver()
else window.setTimeout(update, 1000. / 60.) |> ignore
update()
|
After defining all the helpers, the update
function runs in a loop (via a timer) until there are no dots
left or until the Pacman is out of energy and then it calls one of the continuations.
In the following 5 sections, we'll look at the 5 blocks of code that define the body of the function.
In the first part, the function finds the <canvas>
element, paints it with black background and
creates other graphical elements - namely the game background, ghosts and eyes:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
|
// Fill the canvas element
let canvas = document.getElementsByTagName_canvas().[0]
canvas.width <- 256.
canvas.height <- 256.
let context = canvas.getContext_2d()
context.fillStyle <- U3.Case1 "rgb(0,0,0)"
context.fillRect (0., 0. , 256., 256.);
let bonusImages =
[| createImage Images._200; createImage Images._400;
createImage Images._800; createImage Images._1600 |]
// Load images for rendering
let background = createBackground()
let ghosts = createGhosts(context)
let blue,eyed = createImage Images.blue, createImage Images.eyed
|
Next, we define the game state. Pacman game uses mutable state, so the following uses
F# reference cells; ref 0
creates a mutable cell containing 0. Later, we will access
the value by writing !score
and mutate it by writing score := !score + 1
.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
|
let pills = maze |> Array.map (fun line ->
line.ToCharArray() |> Array.map (fun c -> c))
let dotsLeft = ref (countDots())
let score = ref 0
let bonus = ref 0
let bonuses = ref []
let energy = ref 128
let flashCountdown = ref 0
let powerCountdown = ref 0
let x, y = ref (16 * 8 - 7), ref (23 * 8 - 3)
let v = ref (0,0)
|
At each step of the game, we need to update the positions of ghosts and the Pacman. This is handled
in moveGhosts
and movePacman
:
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:
|
let moveGhosts () =
ghosts |> Array.iter (fun ghost ->
ghost.Move(chooseDirection ghost) )
let movePacman () =
// In which directions should pacman go?
let inputs =
[| if Keyboard.isPressed 81 (*q*) then
yield canGoUp (!x,!y), (0,-1)
if Keyboard.isPressed 65 (*a*) then
yield canGoDown (!x,!y), (0,1)
if Keyboard.isPressed 90 (*z*) then
yield canGoLeft (!x,!y), (-1,0)
if Keyboard.isPressed 88 (*x*) then
yield canGoRight (!x,!y), (1,0) |]
// Can we continue in the same direction?
let canGoForward =
match !v with
| 0,-1 -> canGoUp(!x,!y)
| 0,1 -> canGoDown(!x,!y)
| -1,0 -> canGoLeft(!x,!y)
| 1, 0 -> canGoRight(!x,!y)
| _ -> false
// What new directions can we take?
let availableDirections =
inputs
|> Array.filter fst
|> Array.map snd
|> Array.sortBy (fun v' -> v' = !v)
if availableDirections.Length > 0 then
// Choose the first one, prefers no change
v := availableDirections.[0]
elif inputs.Length = 0 || not canGoForward then
// There are no options - stop
v := 0,0
// Update X and Y accordingly
let x',y' = wrap (!x,!y) !v
x := x'
y := y'
|
The most sophisticated part of the game logic is checking for various events. The following things can happen:
- Pacman eats a small or a large pill - in which case, we play a sound and (optionally) switch to the power mode
- Pacman clashes with a ghost - in which case it either loses energy, or eats the ghost when in power mode
The logic is captured by eatPill
, touchingGhosts
and collisionDetection
functions:
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:
|
// Check if Pacman eats a pill at current cell
let eatPills () =
let tx = int (floor(float ((!x+6)/8)))
let ty = int (floor(float ((!y+6)/8)))
let c = pills.[ty].[tx]
if c = '.' then
// Eating a small pill increments the score
pills.[ty].[tx] <- ' '
clearCell background (tx,ty)
score := !score + 10
decr dotsLeft
sound("./fx/Dot5.wav")
if c = 'o' then
// Eating a large pill turns on the power mode
pills.[ty].[tx] <- ' '
clearCell background (tx,ty)
bonus := 0
score := !score + 50
powerCountdown := 250
decr dotsLeft
sound("./fx/Powerup.wav")
/// Are there any ghosts that collide with Pacman?
let touchingGhosts () =
let px, py = !x, !y
ghosts |> Array.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)) )
|
The collisionDetection
function implements the right response to collision with a ghost:
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:
|
/// Handle collision detections between Pacman and ghosts
let collisionDetection () =
let touched = touchingGhosts ()
if touched.Length > 0 then
if !powerCountdown > 0 then
// Pacman is eating ghosts!
touched |> Array.iter (fun ghost ->
if not ghost.IsReturning then
sound "./fx/EatGhost.wav"
ghost.IsReturning <- true
let added = int (2. ** (float !bonus))
score := !score + added * 200
let image = bonusImages.[!bonus]
bonuses := (100, (image, ghost.X, ghost.Y)) :: !bonuses
bonus := min 3 (!bonus + 1) )
else
// Pacman loses energy when hitting ghosts
decr energy
if !flashCountdown = 0 then sound "./fx/Hurt.wav"
flashCountdown := 30
if !flashCountdown > 0 then decr flashCountdown
/// Updates bonus points
let updateBonus () =
let removals,remainders =
!bonuses
|> List.map (fun (count,x) -> count-1,x)
|> List.partition (fst >> (=) 0)
bonuses := remainders
|
The logic is called from the following single logic
function that includes all the checks:
1:
2:
3:
4:
5:
6:
7:
|
let logic () =
moveGhosts()
movePacman()
eatPills ()
if !powerCountdown > 0 then decr powerCountdown
collisionDetection()
updateBonus ()
|
When rendering everything in the game, we first draw the background and then we render
individual components. Those include the score, remaining energy, pacman, ghosts and bonuses.
Each of those is handled by a single nested function that are put together in render
.
We start with Pacman and remaining energy:
1:
2:
3:
4:
5:
6:
7:
8:
|
let renderPacman () =
let p = Pacman.imageAt(x,y,v)
if (!flashCountdown >>> 1) % 2 = 0
then context.drawImage(U3.Case1 p, float !x, float !y)
let renderEnergy () =
context.fillStyle <- U3.Case1 "yellow"
context.fillRect(120., 250., float !energy, 2.)
|
The next three functions render ghosts, current score and bonuses:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
|
let renderGhosts () =
ghosts |> Array.iter (fun ghost ->
let image =
if ghost.IsReturning then eyed
else
if !powerCountdown = 0 then ghost.Image
elif !powerCountdown > 100 ||
((!powerCountdown >>> 3) % 2) <> 0 then blue
else ghost.Image
context.drawImage(U3.Case1 image, float ghost.X, float ghost.Y) )
let renderScore () =
context.fillStyle <- U3.Case1 "white"
context.font <- "bold 8px";
context.fillText("Score " + (!score).ToString(), 0., 255.)
let renderBonus () =
!bonuses |> List.iter (fun (_,(image,x,y)) ->
context.drawImage(U3.Case1 image, float x, float y))
|
Finally, the render
function puts everything together - note that this needs
to be done in the right order so that we do not accidentally draw dots over a Pacman!
1:
2:
3:
4:
5:
6:
7:
|
let render () =
context.drawImage(U3.Case2 background, 0., 0.)
renderScore ()
renderEnergy ()
renderPacman()
renderGhosts ()
renderBonus ()
|
1:
2:
3:
4:
5:
6:
7:
8:
|
let rec update () =
logic ()
render ()
if !dotsLeft = 0 then onLevelCompleted()
elif !energy <= 0 then onGameOver()
else window.setTimeout(update, 1000. / 60.) |> ignore
update()
|
Now we have everything we need to start the game, so the last step is to define the
levelCompleted
and gameOver
functions (that are called when the game ends), render
the starting state of the game (with "CLICK TO START" text) and start the game!
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:
|
let rec game () =
// Initialize keyboard and canvas
Keyboard.reset()
let canvas = document.getElementsByTagName_canvas().[0]
let context = canvas.getContext_2d()
// A helper function to draw text
let drawText(text,x,y) =
context.fillStyle <- U3.Case1 "white"
context.font <- "bold 8px";
context.fillText(text, x, y)
// Called when level is completed
let levelCompleted () =
drawText("COMPLETED",96.,96.)
window.setTimeout((fun () -> game()),5000.) |> ignore
// Called when the game ends
let gameOver () =
drawText("GAME OVER",96.,96.)
window.setTimeout((fun () -> game()),5000.) |> ignore
// Start a new game after click!
let start () =
let background = createBackground()
context.drawImage(U3.Case2 background, 0., 0.)
context.fillStyle <- U3.Case1 "white"
context.font <- "bold 8px";
drawText("CLICK TO START", 88., 96.)
canvas.onclick <- (fun e ->
canvas.onclick <- null
playLevel (levelCompleted, gameOver)
box true )
// Resize canvas and get ready for a game
let canvas = document.getElementsByTagName_canvas().[0]
canvas.width <- 256.
canvas.height <- 256.
start()
// At the beginnin, initialize keyboard & start the first game.
Keyboard.init ()
game ()
|
namespace Fable
namespace Fable.Core
namespace Fable.Import
module Browser
from Fable.Import
Multiple items
type EmitAttribute =
inherit Attribute
new : macro:string -> EmitAttribute
Full name: Fable.Core.EmitAttribute
--------------------
new : macro:string -> EmitAttribute
val random : unit -> float
Full name: Pacman.random
Multiple items
val float : value:'T -> float (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.float
--------------------
type float = System.Double
Full name: Microsoft.FSharp.Core.float
--------------------
type float<'Measure> = float
Full name: Microsoft.FSharp.Core.float<_>
val failwith : message:string -> 'T
Full name: Microsoft.FSharp.Core.Operators.failwith
val cyand : string
Full name: Pacman.Images.cyand
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAiUlEQVQoU8WSURKAIAhE8Sh6Fc/tVfQoJdqiMDTVV4wfufAAmw3kxEHUz4pA1I8OJVjAKZZ6+XiC0ATTB/gW2mEFtlpHLqaktrQ6TxUQSRCAPX2AWPMLyM0VmPOcV8palxt6uoAMpDjfWJt+o6cr0DPDnfYjyL94NwIcYjXcR/FuYklcxrZ3OO0Ep4dJ/3dR5jcAAAAASUVORK5CYII="
val oranged : string
Full name: Pacman.Images.oranged
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAgklEQVQoU8WS0RGAIAxDZRRYhblZBUZBsBSaUk/9kj9CXlru4g7r1FxBdsFpGwoa2NwrYIFPEIeM6QS+hQQMYC70EjzuuOlt6gT5kRGGTf0Cx5qfwJYOYIw0L6W1bg+09Al2wAcCS8Y/WjqAZhluxD/B3ghZBO6n1sadzLLEbNSg8pzXIVLvbNvPwAAAAABJRU5ErkJggg=="
let pinkd = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAj0lEQVQoU8WSsRWAIAxEZRQpXITGVZzIVWxYxAJHwRfwMInxqZV0XPIvgXeuM05eUuayG73TbULQwKWZGTTwCYIJphfwLcRhAW5DLfWrXFLrNLWBKAIBbOkFxJpfQDIXYAh1XoznumRo6Q0kwE8VTLN8o6UL0ArDnfYjSF/Mg4CEaA330sxD3ApHLvUdSdsBdgNkr9L8gxYAAAAASUVORK5CYII="
let redd = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAkklEQVQoU8WSvRWAIAyEZRQtXIRCV3EiVtGCRSx0FHxBD5MYn1pJl0u+/PDOVcZLY5e47PrJ6TIhaOBSzBoU8AlCE0zP4FuIwwJc25Bz9TyILbVOUwuIJAjAlp5BrPkFpOYC9H6fF+O5LjW09AIS0Az7jUuQN1q6AC0z3Gk/gvTF3AhwiNYQ52Ju4pI4fKljOG0DA3tp97vN6C8AAAAASUVORK5CYII="
let pu1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAAAWElEQVQoU62SUQoAIAhD9f6HNiYYolYi9VfzuXIxDRYbI0LCTHsfe3ldi3BgRRUY9Rnku1Rupf4NgiPeVjVU7STckphBceSvrHHtNPI21HWz4NO3eUUAgwVpmjX/zwK8KQAAAABJRU5ErkJggg=="
let pu2 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAAAW0lEQVQoU8WSwQoAIAhD9f8/2lIwdKRIl7o1e010THBESJiJXca76qnoDxFC3SD9LRpWkLnsLt4gdImtlLX/EK4iDapqr4VuI2+BauQjaOrmSz8xillDp5gQrS054jv/0fkNVAAAAABJRU5ErkJggg=="
let pd1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAAAXElEQVQoU62SUQoAIAhD9f6HNgyMWpMs6k/XU5mqwDMTw5yq6JwbAfucwR2qAFHAu75BN11Gt6+Qz54VpMJsMV3BaS9UR8txkUzfLC9DUY0BYbOPGfpyU3g2WdwAOvU1/9KZsT4AAAAASUVORK5CYII="
let pd2 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAAAU0lEQVQoU62SUQoAIAhD9f6HNgwUGw4s6q/pc6KqwDMTQ01VtGr56ZIZvKEJEAXc9Q26cUm3r5D3zgrywHeoG3ldJrZIRz6C0I1BoR83FTBCeHsLIlw7/wOkQycAAAAASUVORK5CYII="
let pl1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAAAVUlEQVQoU62S2woAIAhD9f8/2jAwvGRMyDfF49iQKZUISZ4xE/vZaW7LHbwhBLADqjpSUjBAdglRDQa9hxfcQi+vf5RGnpDlkB4KlMgR0N6pBIH83gIPFCb/N+MLCwAAAABJRU5ErkJggg=="
let pl2 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAAAUklEQVQoU52SUQoAIAhD3f0PbRQoZgnT/hyttYeQdFRFswYIoubD73JlPibGYA/s1Jmpk+JpDIinWxbiXP3iQslCwbhTxzhHbsWZNFsnCkTevQW2bCb/VRTuVwAAAABJRU5ErkJggg=="
let pr1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAAAWElEQVQoU52S4Q4AIASE3fs/tKalSTHyL/O5CyAXzMQ+BxBsbj9exRE8oQqgDUS1BalNVFSuP2WQL94WIygCBEzttZWOvbz2VBnGtLXg1sgV/L8I679yewN9sScO5wcxLQAAAABJRU5ErkJggg=="
let pr2 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAAAVElEQVQoU62SWwoAIAgE9f6HNgqU3BK2R3+J48KoCjwzMaypis61+OyaK3hADOADeuoddJISaQy0iKggbEz2viah7mVPTNq7cp/ApLmcdFPVdaDJBnWdJwjk629HAAAAAElFTkSuQmCC"
let blue = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAeklEQVQoU62S0Q3AIAhEyyi6UcfoRB2jG+koNkeCoVcaTaw/huMeEkS24KTUmpdrFWHbQ2CAzb5AB0eQFTFYwVnIw/+B5by0cD52vTmGhnaF25wBAb/A6HsibR0ctch5fRHi1zCigvCut4oR+wnbhrBmsZr9DlqCQfbcnfZjDyiZqCEAAAAASUVORK5CYII="
let eyed = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAUElEQVQoU2NkIBMwkqmPYYA13rt37z/I6UpKSiguwSYOVwCThPkZphmXOHU0OjtD7Nu7F+FckI3YxFH8oqgI8eP9+6h+xCY+wNFBSiqiv1MBDgYsD185vj8AAAAASUVORK5CYII="
let _200 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAS0lEQVQoU2NkIBMwkqmPYYA0vpVR+Q9zsvCTO4yE+CC1KE4FaYBpxEfDNWKzgWiNIIUw5xKyGa+N+PyM4UdS4nSA4pEUJ8LUku1UAMC0VA8iscBNAAAAAElFTkSuQmCC"
let _400 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAASElEQVQoU2NkIBMwkqmPYYA0vpVR+S/85A4jMg3zAkwcmQ9ig52KTSO6Qch8FI3oNhClEaaJWJvhNmLTSJQfyYnLAYpHujoVAChTXA9pVJi5AAAAAElFTkSuQmCC"
let _800 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAQElEQVQoU2NkIBMwkqmPYYA0vpVR+Q9zsvCTO4yE+CC1YKeCFMI0EEOjaES3EZ8BtLERn5/hNpITlwMUj3R1KgCe5lwPHtUmcwAAAABJRU5ErkJggg=="
let _1600 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAQ0lEQVQoU2NkIBMwkqmPYQA0vpVR+S/85A4jiIY5mxg+WANMIYiGaUYXR+ejaES3EdlAvBrxKSTJRnx+HoDoGDopBwDHLGwPAhDgRQAAAABJRU5ErkJggg=="
val createImage : data:string -> HTMLImageElement
Full name: Pacman.createImage
val data : string
val img : HTMLImageElement
val document : Document
Full name: Fable.Import.Browser.document
abstract member Document.createElement_img : unit -> HTMLImageElement
property HTMLImageElement.src: string
val maze : string []
Full name: Pacman.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##").Split(',')
val tileBits : int [] []
Full name: Pacman.tileBits
// tl
[|0b00000000;0b00000000;0b00000000;0b00000000;0b11111111;0b00000000;0b00000000;0b00000000|] // top
[|0b00000000;0b00000000;0b00000000;0b00000000;0b11000000;0b00100000;0b00010000;0b00010000|] // tr
[|0b00001000;0b00001000;0b00001000;0b00001000;0b00001000;0b00001000;0b00001000;0b00001000|] // left
[|0b00010000;0b00010000;0b00010000;0b00010000;0b00010000;0b00010000;0b00010000;0b00010000|] // right
[|0b00001000;0b00001000;0b00000100;0b00000011;0b00000000;0b00000000;0b00000000;0b00000000|] // bl
[|0b00000000;0b00000000;0b00000000;0b11111111;0b00000000;0b00000000;0b00000000;0b00000000|] // bottom
[|0b00010000;0b00010000;0b00100000;0b11000000;0b00000000;0b00000000;0b00000000;0b00000000|] // br
[|0b00000000;0b00000000;0b00000000;0b00000000;0b11111111;0b00000000;0b00000000;0b00000000|] // door
[|0b00000000;0b00000000;0b00000000;0b00011000;0b00011000;0b00000000;0b00000000;0b00000000|] // pill
[|0b00000000;0b00011000;0b00111100;0b01111110;0b01111110;0b00111100;0b00011000;0b00000000|] // power
val blank : int []
Full name: Pacman.blank
0b00000000;0b00000000;0b00000000;0b00000000;0b00000000
val isWall : c:char -> bool
Full name: Pacman.isWall
Characters _|!/7LJ represent different walls
val c : char
Multiple items
val char : value:'T -> char (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.char
--------------------
type char = System.Char
Full name: Microsoft.FSharp.Core.char
val tileAt : x:int * y:int -> char
Full name: Pacman.tileAt
Returns ' ' for positions outside of range
val x : int
val y : int
val isWallAt : x:int * y:int -> bool
Full name: Pacman.isWallAt
Is the maze tile at x,y a wall?
val verticallyAligned : x:int * y:'a -> bool
Full name: Pacman.verticallyAligned
val y : 'a
val horizontallyAligned : x:'a * y:int -> bool
Full name: Pacman.horizontallyAligned
val x : 'a
val isAligned : n:int -> bool
Full name: Pacman.isAligned
val n : int
val noWall : x:int * y:int -> ex:int * ey:int -> bool
Full name: Pacman.noWall
val ex : int
val ey : int
val bx : int
val by : int
val not : value:bool -> bool
Full name: Microsoft.FSharp.Core.Operators.not
val canGoUp : x:int * y:int -> bool
Full name: Pacman.canGoUp
val canGoDown : x:int * y:int -> bool
Full name: Pacman.canGoDown
val canGoLeft : x:int * y:int -> bool
Full name: Pacman.canGoLeft
val canGoRight : x:int * y:int -> bool
Full name: Pacman.canGoRight
val tileColors : string
Full name: Pacman.tileColors
val tileChars : string
Full name: Pacman.tileChars
val toTile : c:char -> int [] * char
Full name: Pacman.toTile
Returns tile for a given Maze character
val i : int
System.String.IndexOf(value: string) : int
System.String.IndexOf(value: char) : int
System.String.IndexOf(value: string, comparisonType: System.StringComparison) : int
System.String.IndexOf(value: string, startIndex: int) : int
System.String.IndexOf(value: char, startIndex: int) : int
System.String.IndexOf(value: string, startIndex: int, comparisonType: System.StringComparison) : int
System.String.IndexOf(value: string, startIndex: int, count: int) : int
System.String.IndexOf(value: char, startIndex: int, count: int) : int
System.String.IndexOf(value: string, startIndex: int, count: int, comparisonType: System.StringComparison) : int
val draw : f:(int * int -> unit) -> lines:int [] -> unit
Full name: Pacman.draw
Draw the lines specified by a wall tile
val f : (int * int -> unit)
val lines : int []
Multiple items
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
val width : int
module Array
from Microsoft.FSharp.Collections
val iteri : action:(int -> 'T -> unit) -> array:'T [] -> unit
Full name: Microsoft.FSharp.Collections.Array.iteri
val line : int
val bit : int
val pattern : int
val createBrush : context:CanvasRenderingContext2D -> r:int * g:int * b:int * a:int -> ImageData
Full name: Pacman.createBrush
Creates a brush for rendering the given RGBA color
val context : CanvasRenderingContext2D
Multiple items
val CanvasRenderingContext2D : CanvasRenderingContext2DType
Full name: Fable.Import.Browser.CanvasRenderingContext2D
--------------------
type CanvasRenderingContext2D =
interface
abstract member arc : x:float * y:float * radius:float * startAngle:float * endAngle:float * ?anticlockwise:bool -> unit
abstract member arcTo : x1:float * y1:float * x2:float * y2:float * radius:float -> unit
abstract member beginPath : unit -> unit
abstract member bezierCurveTo : cp1x:float * cp1y:float * cp2x:float * cp2y:float * x:float * y:float -> unit
abstract member clearRect : x:float * y:float * w:float * h:float -> unit
abstract member clip : ?fillRule:string -> unit
abstract member closePath : unit -> unit
abstract member createImageData : imageDataOrSw:U2<float,ImageData> * ?sh:float -> ImageData
abstract member createLinearGradient : x0:float * y0:float * x1:float * y1:float -> CanvasGradient
abstract member createPattern : image:U3<HTMLImageElement,HTMLCanvasElement,HTMLVideoElement> * repetition:string -> CanvasPattern
...
end
Full name: Fable.Import.Browser.CanvasRenderingContext2D
val r : int
val g : int
val b : int
val a : int
val id : ImageData
abstract member CanvasRenderingContext2D.createImageData : imageDataOrSw:U2<float,ImageData> * ?sh:float -> ImageData
type U2<'a,'b> =
| Case1 of 'a
| Case2 of 'b
Full name: Fable.Core.U2<_,_>
union case U2.Case1: 'a -> U2<'a,'b>
val d : Fable.Import.JS.Uint8ClampedArray
property ImageData.data: Fable.Import.JS.Uint8ClampedArray
val createBackground : unit -> HTMLCanvasElement
Full name: Pacman.createBackground
val background : HTMLCanvasElement
abstract member Document.createElement_canvas : unit -> HTMLCanvasElement
property HTMLCanvasElement.width: float
property HTMLCanvasElement.height: float
abstract member HTMLCanvasElement.getContext_2d : unit -> CanvasRenderingContext2D
property CanvasRenderingContext2D.fillStyle: U3<string,CanvasGradient,CanvasPattern>
type U3<'a,'b,'c> =
| Case1 of 'a
| Case2 of 'b
| Case3 of 'c
Full name: Fable.Core.U3<_,_,_>
union case U3.Case1: 'a -> U3<'a,'b,'c>
abstract member CanvasRenderingContext2D.fillRect : x:float * y:float * w:float * h:float -> unit
val blue : ImageData
val yellow : ImageData
val lines : string []
property System.Array.Length: int
val line : string
property System.String.Length: int
val tile : int []
val color : char
val brush : ImageData
val x' : int
val y' : int
abstract member CanvasRenderingContext2D.putImageData : imagedata:ImageData * dx:float * dy:float * ?dirtyX:float * ?dirtyY:float * ?dirtyWidth:float * ?dirtyHeight:float -> unit
val clearCell : background:HTMLCanvasElement -> x:int * y:int -> unit
Full name: Pacman.clearCell
Clear whatever is rendered in the specified Maze cell
Multiple items
val HTMLCanvasElement : HTMLCanvasElementType
Full name: Fable.Import.Browser.HTMLCanvasElement
--------------------
type HTMLCanvasElement =
interface
inherit HTMLElement
abstract member getContext : contextId:string * [<ParamArray>] args:obj [] -> U2<CanvasRenderingContext2D,WebGLRenderingContext>
abstract member getContext_2d : unit -> CanvasRenderingContext2D
abstract member ( getContext_experimental-webgl ) : unit -> WebGLRenderingContext
abstract member height : float
abstract member width : float
abstract member msToBlob : unit -> Blob
abstract member height : float with set
abstract member width : float with set
abstract member toBlob : unit -> Blob
...
end
Full name: Fable.Import.Browser.HTMLCanvasElement
val wrap : x:int * y:int -> dx:int * dy:int -> int * int
Full name: Pacman.wrap
Wrap around the sides of the Maze
val dx : int
val dy : int
Multiple items
type Ghost =
new : image:HTMLImageElement * x:int * y:int * v:(int * int) -> Ghost
member Move : v:(int * int) -> unit
member Reset : unit -> unit
member Image : HTMLImageElement
member IsReturning : bool
member V : int * int
member X : int
member Y : int
member IsReturning : bool with set
Full name: Pacman.Ghost
Mutable representation of a ghost
--------------------
new : image:HTMLImageElement * x:int * y:int * v:(int * int) -> Ghost
val image : HTMLImageElement
Multiple items
val HTMLImageElement : HTMLImageElementType
Full name: Fable.Import.Browser.HTMLImageElement
--------------------
type HTMLImageElement =
interface
inherit HTMLElement
abstract member align : string
abstract member alt : string
abstract member border : string
abstract member complete : bool
abstract member crossOrigin : string
abstract member currentSrc : string
abstract member height : float
abstract member hspace : float
abstract member isMap : bool
...
end
Full name: Fable.Import.Browser.HTMLImageElement
val v : int * int
val mutable x' : int
val mutable y' : int
val mutable v' : int * int
val Image : ImageType
Full name: Fable.Import.Browser.Image
val set : elements:seq<'T> -> Set<'T> (requires comparison)
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.set
member Ghost.X : int
Full name: Pacman.Ghost.X
val __ : Ghost
member Ghost.Y : int
Full name: Pacman.Ghost.Y
member Ghost.V : int * int
Full name: Pacman.Ghost.V
val ghost : Ghost
member Ghost.Reset : unit -> unit
Full name: Pacman.Ghost.Reset
Move back to initial location
member Ghost.Move : v:(int * int) -> unit
Full name: Pacman.Ghost.Move
Move in the current direction
val createGhosts : context:'a -> Ghost []
Full name: Pacman.createGhosts
val context : 'a
module Images
from Pacman
val redd : string
Full name: Pacman.Images.redd
val pinkd : string
Full name: Pacman.Images.pinkd
val map : mapping:('T -> 'U) -> array:'T [] -> 'U []
Full name: Microsoft.FSharp.Collections.Array.map
val flood : canFill:(int * int -> bool) -> fill:(int * int * int -> unit) -> x:int * y:int -> unit
Full name: Pacman.flood
Recursive flood fill function
val canFill : (int * int -> bool)
val fill : (int * int * int -> unit)
val f : (int -> (int * int) list -> unit)
val ps : (int * int) list
Multiple items
module List
from Microsoft.FSharp.Collections
--------------------
type List<'T> =
| ( [] )
| ( :: ) of Head: 'T * Tail: 'T list
interface IEnumerable
interface IEnumerable<'T>
member GetSlice : startIndex:int option * endIndex:int option -> 'T list
member Head : 'T
member IsEmpty : bool
member Item : index:int -> 'T with get
member Length : int
member Tail : 'T list
static member Cons : head:'T * tail:'T list -> 'T list
static member Empty : 'T list
Full name: Microsoft.FSharp.Collections.List<_>
val filter : predicate:('T -> bool) -> list:'T list -> 'T list
Full name: Microsoft.FSharp.Collections.List.filter
val iter : action:('T -> unit) -> list:'T list -> unit
Full name: Microsoft.FSharp.Collections.List.iter
val collect : mapping:('T -> 'U list) -> list:'T list -> 'U list
Full name: Microsoft.FSharp.Collections.List.collect
val route_home : int [] []
Full name: Pacman.route_home
Possible routes that take the ghost home
val numbers : int [] []
System.String.ToCharArray() : char []
System.String.ToCharArray(startIndex: int, length: int) : char []
val fillValue : x:int * y:int -> ex:int * ey:int -> int
Full name: Pacman.fillValue
Find the shortest way home from specified location
(adjusted by offset in which ghosts start)
val floor : value:'T -> 'T (requires member Floor)
Full name: Microsoft.FSharp.Core.Operators.floor
val fillUp : x:int * y:int -> int
Full name: Pacman.fillUp
val fillDown : x:int * y:int -> int
Full name: Pacman.fillDown
val fillLeft : x:int * y:int -> int
Full name: Pacman.fillLeft
val fillRight : x:int * y:int -> int
Full name: Pacman.fillRight
val chooseDirection : ghost:Ghost -> int * int
Full name: Pacman.chooseDirection
property Ghost.X: int
property Ghost.Y: int
property Ghost.V: int * int
val isBackwards : (int * int -> bool)
val directions : ((int * int) * int) []
property Ghost.IsReturning: bool
val xs : ((int * int) * int) []
val sortBy : projection:('T -> 'Key) -> array:'T [] -> 'T [] (requires comparison)
Full name: Microsoft.FSharp.Collections.Array.sortBy
val snd : tuple:('T1 * 'T2) -> 'T2
Full name: Microsoft.FSharp.Core.Operators.snd
val xs : (int * int) []
val fst : tuple:('T1 * 'T2) -> 'T1
Full name: Microsoft.FSharp.Core.Operators.fst
val filter : predicate:('T -> bool) -> array:'T [] -> 'T []
Full name: Microsoft.FSharp.Collections.Array.filter
val i : float
val mutable keysPressed : Set<int>
Full name: Pacman.Keyboard.keysPressed
Set of currently pressed keys
Multiple items
module Set
from Microsoft.FSharp.Collections
--------------------
type Set<'T (requires comparison)> =
interface IComparable
interface IEnumerable
interface IEnumerable<'T>
interface ICollection<'T>
new : elements:seq<'T> -> Set<'T>
member Add : value:'T -> Set<'T>
member Contains : value:'T -> bool
override Equals : obj -> bool
member IsProperSubsetOf : otherSet:Set<'T> -> bool
member IsProperSupersetOf : otherSet:Set<'T> -> bool
...
Full name: Microsoft.FSharp.Collections.Set<_>
--------------------
new : elements:seq<'T> -> Set<'T>
val empty<'T (requires comparison)> : Set<'T> (requires comparison)
Full name: Microsoft.FSharp.Collections.Set.empty
val reset : unit -> unit
Full name: Pacman.Keyboard.reset
Update the keys as requested
val isPressed : keyCode:int -> bool
Full name: Pacman.Keyboard.isPressed
val keyCode : int
val contains : element:'T -> set:Set<'T> -> bool (requires comparison)
Full name: Microsoft.FSharp.Collections.Set.contains
val update : e:KeyboardEvent * pressed:bool -> 'a (requires 'a : null)
Full name: Pacman.Keyboard.update
Triggered when key is pressed/released
val e : KeyboardEvent
Multiple items
val KeyboardEvent : KeyboardEventType
Full name: Fable.Import.Browser.KeyboardEvent
--------------------
type KeyboardEvent =
interface
inherit UIEvent
abstract member getModifierState : keyArg:string -> bool
abstract member DOM_KEY_LOCATION_JOYSTICK : float
abstract member DOM_KEY_LOCATION_LEFT : float
abstract member DOM_KEY_LOCATION_MOBILE : float
abstract member DOM_KEY_LOCATION_NUMPAD : float
abstract member DOM_KEY_LOCATION_RIGHT : float
abstract member DOM_KEY_LOCATION_STANDARD : float
abstract member altKey : bool
abstract member char : string
...
end
Full name: Fable.Import.Browser.KeyboardEvent
val pressed : bool
property KeyboardEvent.keyCode: float
val op : (int -> Set<int> -> Set<int>)
val add : value:'T -> set:Set<'T> -> Set<'T> (requires comparison)
Full name: Microsoft.FSharp.Collections.Set.add
val remove : value:'T -> set:Set<'T> -> Set<'T> (requires comparison)
Full name: Microsoft.FSharp.Collections.Set.remove
val init : unit -> unit
Full name: Pacman.Keyboard.init
Register DOM event handlers
val window : Window
Full name: Fable.Import.Browser.window
abstract member Window.addEventListener_keydown : listener:System.Func<KeyboardEvent,obj> * ?useCapture:bool -> unit
abstract member Window.addEventListener_keyup : listener:System.Func<KeyboardEvent,obj> * ?useCapture:bool -> unit
val private pu1 : HTMLImageElement
Full name: Pacman.Pacman.pu1
val pu2 : HTMLImageElement
Full name: Pacman.Pacman.pu2
val pu1 : string
Full name: Pacman.Images.pu1
val pu2 : string
Full name: Pacman.Images.pu2
val private pd1 : HTMLImageElement
Full name: Pacman.Pacman.pd1
val pd2 : HTMLImageElement
Full name: Pacman.Pacman.pd2
val pd1 : string
Full name: Pacman.Images.pd1
val pd2 : string
Full name: Pacman.Images.pd2
val private pl1 : HTMLImageElement
Full name: Pacman.Pacman.pl1
val pl2 : HTMLImageElement
Full name: Pacman.Pacman.pl2
val pl1 : string
Full name: Pacman.Images.pl1
val pl2 : string
Full name: Pacman.Images.pl2
val private pr1 : HTMLImageElement
Full name: Pacman.Pacman.pr1
val pr2 : HTMLImageElement
Full name: Pacman.Pacman.pr2
val pr1 : string
Full name: Pacman.Images.pr1
val pr2 : string
Full name: Pacman.Images.pr2
val private lastp : HTMLImageElement ref
Full name: Pacman.Pacman.lastp
Multiple items
val ref : value:'T -> 'T ref
Full name: Microsoft.FSharp.Core.Operators.ref
--------------------
type 'T ref = Ref<'T>
Full name: Microsoft.FSharp.Core.ref<_>
val imageAt : x:int ref * y:int ref * v:(int * int) ref -> HTMLImageElement
Full name: Pacman.Pacman.imageAt
val x : int ref
val y : int ref
val v : (int * int) ref
val p1 : HTMLImageElement
val p2 : HTMLImageElement
val p : HTMLImageElement
val sound : file:string -> unit
Full name: Pacman.sound
Uses Fable's Emit to call JavaScript directly
val file : string
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
type unit = Unit
Full name: Microsoft.FSharp.Core.unit
val countDots : unit -> int
Full name: Pacman.countDots
Count number of dots in the maze
val sumBy : projection:('T -> 'U) -> array:'T [] -> 'U (requires member ( + ) and member get_Zero)
Full name: Microsoft.FSharp.Collections.Array.sumBy
val playLevel : onLevelCompleted:(unit -> unit) * onGameOver:(unit -> unit) -> unit
Full name: pacman.playLevel
val onLevelCompleted : (unit -> unit)
val onGameOver : (unit -> unit)
val update : (unit -> unit)
val ignore : value:'T -> unit
Full name: Microsoft.FSharp.Core.Operators.ignore
val playLevel : onLevelCompleted:(unit -> unit) * onGameOver:(unit -> unit) -> unit
Full name: Pacman.playLevel
val canvas : HTMLCanvasElement
abstract member Document.getElementsByTagName_canvas : unit -> NodeListOf<HTMLCanvasElement>
val bonusImages : HTMLImageElement []
val _200 : string
Full name: Pacman.Images._200
val _400 : string
Full name: Pacman.Images._400
val _800 : string
Full name: Pacman.Images._800
val _1600 : string
Full name: Pacman.Images._1600
val ghosts : Ghost []
val blue : HTMLImageElement
val eyed : HTMLImageElement
val blue : string
Full name: Pacman.Images.blue
val eyed : string
Full name: Pacman.Images.eyed
val pills : char [] []
val dotsLeft : int ref
val score : int ref
val bonus : int ref
val bonuses : (int * (HTMLImageElement * int * int)) list ref
val energy : int ref
val flashCountdown : int ref
val powerCountdown : int ref
val moveGhosts : (unit -> unit)
val iter : action:('T -> unit) -> array:'T [] -> unit
Full name: Microsoft.FSharp.Collections.Array.iter
member Ghost.Move : v:(int * int) -> unit
Move in the current direction
val movePacman : (unit -> unit)
val inputs : (bool * (int * int)) []
module Keyboard
from Pacman
val canGoForward : bool
val availableDirections : (int * int) []
val v' : int * int
val eatPills : (unit -> unit)
val tx : int
val ty : int
val decr : cell:int ref -> unit
Full name: Microsoft.FSharp.Core.Operators.decr
val touchingGhosts : (unit -> Ghost [])
Are there any ghosts that collide with Pacman?
val px : int
val py : int
val collisionDetection : (unit -> unit)
Handle collision detections between Pacman and ghosts
val touched : Ghost []
val added : int
val min : e1:'T -> e2:'T -> 'T (requires comparison)
Full name: Microsoft.FSharp.Core.Operators.min
val updateBonus : (unit -> unit)
Updates bonus points
val removals : (int * (HTMLImageElement * int * int)) list
val remainders : (int * (HTMLImageElement * int * int)) list
val map : mapping:('T -> 'U) -> list:'T list -> 'U list
Full name: Microsoft.FSharp.Collections.List.map
val count : int
val x : HTMLImageElement * int * int
val partition : predicate:('T -> bool) -> list:'T list -> 'T list * 'T list
Full name: Microsoft.FSharp.Collections.List.partition
val logic : (unit -> unit)
val renderPacman : (unit -> unit)
module Pacman
from Pacman
abstract member CanvasRenderingContext2D.drawImage : image:U3<HTMLImageElement,HTMLCanvasElement,HTMLVideoElement> * offsetX:float * offsetY:float * ?width:float * ?height:float * ?canvasOffsetX:float * ?canvasOffsetY:float * ?canvasImageWidth:float * ?canvasImageHeight:float -> unit
val renderEnergy : (unit -> unit)
val renderGhosts : (unit -> unit)
property Ghost.Image: HTMLImageElement
val renderScore : (unit -> unit)
property CanvasRenderingContext2D.font: string
abstract member CanvasRenderingContext2D.fillText : text:string * x:float * y:float * ?maxWidth:float -> unit
val renderBonus : (unit -> unit)
val render : (unit -> unit)
union case U3.Case2: 'b -> U3<'a,'b,'c>
abstract member WindowTimers.setTimeout : handler:obj * ?timeout:obj * [<System.ParamArray>] args:obj [] -> float
val game : unit -> unit
Full name: Pacman.game
val drawText : (string * float * float -> unit)
val text : string
val x : float
val y : float
val levelCompleted : (unit -> unit)
val gameOver : (unit -> unit)
val start : (unit -> unit)
property HTMLElement.onclick: System.Func<MouseEvent,obj>
val e : MouseEvent
val box : value:'T -> obj
Full name: Microsoft.FSharp.Core.Operators.box