Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
HenJi committed Oct 19, 2011
0 parents commit d682679
Show file tree
Hide file tree
Showing 8 changed files with 512 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
EXE = opacman.exe

all: $(EXE)

$(EXE): src/*.opa #resources/*
opa src/*.opa -o $(EXE)

clean:
rm -Rf *.exe _build _tracks *.log **/#*#
44 changes: 44 additions & 0 deletions src/base.opa
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
@client Base = {{

Dir = {{

facing_angle(dir:Base.direction) =
match dir with
| {up} -> -Math.PI/2.
| {down} -> Math.PI/2.
| {left} -> Math.PI
| {right} -> 0.
| {still} -> 0.

deltas(dir:Base.direction) =
match dir with
| {up} -> (0, -1)
| {down} -> (0, 1)
| {left} -> (-1, 0)
| {right} -> (1, 0)
| {still} -> (0, 0)

back(dir:Base.direction):Base.direction =
match dir with
| {up} -> {down}
| {down} -> {up}
| {left} -> {right}
| {right} -> {left}
| x -> x

}}

@both make(x, y, dir, max_steps) = {
pos = ~{x y}
cur_step = 0
~dir ~max_steps
}

center(b:Base.t) =
w = base_size
d = (w*b.cur_step) / b.max_steps
(dx, dy) = Dir.deltas(b.dir)
(1+w/2+w*b.pos.x+d*dx,
1+w/2+w*b.pos.y+d*dy)

}}
54 changes: 54 additions & 0 deletions src/food.opa
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
(initial_food, walls) = [
[1,1,1,1,1,1,1,1,1,1,1,1,8,8,1,1,1,1,1,1,1,1,1,1,1,1],
[2,8,8,8,8,1,8,8,8,8,8,1,8,8,1,8,8,8,8,8,1,8,8,8,8,2],
[1,8,8,8,8,1,8,8,8,8,8,1,8,8,1,8,8,8,8,8,1,8,8,8,8,1],
[1,8,8,8,8,1,8,8,8,8,8,1,8,8,1,8,8,8,8,8,1,8,8,8,8,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,8,8,8,8,1,8,8,1,8,8,8,8,8,8,8,8,1,8,8,1,8,8,8,8,1],
[1,8,8,8,8,1,8,8,1,8,8,8,8,8,8,8,8,1,8,8,1,8,8,8,8,1],
[1,1,1,1,1,1,8,8,1,1,1,1,8,8,1,1,1,1,8,8,1,1,1,1,1,1],
[8,8,8,8,8,1,8,8,8,8,8,0,8,8,0,8,8,8,8,8,1,8,8,8,8,8],
[8,8,8,8,8,1,8,8,8,8,8,0,8,8,0,8,8,8,8,8,1,8,8,8,8,8],
[8,8,8,8,8,1,8,8,0,0,0,0,0,0,0,0,0,0,8,8,1,8,8,8,8,8],
[8,8,8,8,8,1,8,8,0,8,8,8,0,0,8,8,8,0,8,8,1,8,8,8,8,8],
[8,8,8,8,8,1,8,8,0,8,0,0,8,8,0,0,8,0,8,8,1,8,8,8,8,8],
[0,0,0,0,8,1,0,0,0,8,0,0,0,0,0,0,8,0,0,0,1,8,0,0,0,0],
[8,8,8,8,8,1,8,8,0,8,0,0,0,0,0,0,8,0,8,8,1,8,8,8,8,8],
[8,8,8,8,8,1,8,8,0,8,8,8,8,8,8,8,8,0,8,8,1,8,8,8,8,8],
[8,8,8,8,8,1,8,8,0,0,0,0,0,0,0,0,0,0,8,8,1,8,8,8,8,8],
[8,8,8,8,8,1,8,8,0,8,8,8,8,8,8,8,8,0,8,8,1,8,8,8,8,8],
[8,8,8,8,8,1,8,8,0,8,8,8,8,8,8,8,8,0,8,8,1,8,8,8,8,8],
[1,1,1,1,1,1,1,1,1,1,1,1,8,8,1,1,1,1,1,1,1,1,1,1,1,1],
[1,8,8,8,8,1,8,8,8,8,8,1,8,8,1,8,8,8,8,8,1,8,8,8,8,1],
[2,8,8,8,8,1,8,8,8,8,8,1,8,8,1,8,8,8,8,8,1,8,8,8,8,2],
[1,1,1,8,8,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,8,8,1,1,1],
[8,8,1,8,8,1,8,8,1,8,8,8,8,8,8,8,8,1,8,8,1,8,8,1,8,8],
[8,8,1,8,8,1,8,8,1,8,8,8,8,8,8,8,8,1,8,8,1,8,8,1,8,8],
[1,1,1,1,1,1,8,8,1,1,1,1,8,8,1,1,1,1,8,8,1,1,1,1,1,1],
[1,8,8,8,8,8,8,8,8,8,8,1,8,8,1,8,8,8,8,8,8,8,8,8,8,1],
[1,8,8,8,8,8,8,8,8,8,8,1,8,8,1,8,8,8,8,8,8,8,8,8,8,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
] |> List.foldi(
y, l, (food, walls) ->
List.foldi(
x, v, (food, walls) ->
if v == 1 then (Set.add(~{x y}, food), walls)
else if v == 8 then (food, Set.add(~{x y}, walls))
else (food, walls),
l, (food, walls)),
_, (Set.empty:set(Base.pos), Set.empty:set(Base.pos)))

@client Food = {{

draw(ctx:Canvas.context) =
food = game.get().food
w = base_size
do Canvas.save(ctx)
do Canvas.set_fill_style(ctx, {color=Color.red})
do Set.iter(
~{x y} -> Canvas.fill_rect(ctx, w/2+x*w-2, w/2+y*w-2, 6, 6),
food)
do Canvas.restore(ctx)
void

}}
125 changes: 125 additions & 0 deletions src/ghost.opa
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
@client Ghost = {{

@server default = [
{ ai = {dumb}
base = Base.make(5, 4, {right}, 10)
color = Color.orange },
{ ai = {guard}
base = Base.make(20, 4, {down}, 10)
color = Color.darkred },
{ ai = {dumb}
base = Base.make(20, 22, {left}, 10)
color = Color.gold },
{ ai = {guard}
base = Base.make(5, 22, {up}, 10)
color = Color.green }
] : list(Ghost.t)

invert_color(c:color) =
Color.set_r(c, 255-Color.r(c))
|> Color.set_g(_, 255-Color.g(c))
|> Color.set_b(_, 255-Color.b(c))

draw_one(ctx:Canvas.context, g:Ghost.t) =
w = base_size

do Canvas.save(ctx)
do Canvas.set_fill_style(ctx, {color=g.color})
(center_x, center_y) = Base.center(g.base)
do Canvas.translate(ctx, center_x, center_y)

do Canvas.begin_path(ctx)
do Canvas.move_to(ctx, w/2, 0)
do Canvas.quadratic_curve_to(ctx, w/2, -w/2, 0, -w/2)
do Canvas.quadratic_curve_to(ctx, -w/2, -w/2, -w/2, 0)
do Canvas.line_to(ctx, -w/2, w/2)
do Canvas.line_to(ctx, -w/6, w/3)
do Canvas.line_to(ctx, 0, w/2)
do Canvas.line_to(ctx, w/6, w/3)
do Canvas.line_to(ctx, w/2, w/2)
do Canvas.fill(ctx)

do Canvas.clear_rect(ctx, -w/4, -w/4, w/2, w/4)
do Canvas.set_fill_style(ctx, {color=invert_color(g.color)})
dx =
base = g.base.max_steps
step =
if g.base.cur_step > base/2 then base - g.base.cur_step
else g.base.cur_step
(w*step)/(2*base)
do Canvas.fill_rect(ctx, dx-w/4, -w/4, w/4, w/4)

do Canvas.restore(ctx)
void

@private build_move_options(b:Base.t, no_back) =
all_options = [] : list(Base.direction)
|> (if Wall.at(b.pos.x+1, b.pos.y) then identity
else List.add({right}, _))
|> (if Wall.at(b.pos.x-1, b.pos.y) then identity
else List.add({left}, _))
|> (if Wall.at(b.pos.x, b.pos.y+1) then identity
else List.add({down}, _))
|> (if Wall.at(b.pos.x, b.pos.y-1) then identity
else List.add({up}, _))
if List.length(all_options) == 1 then all_options
else if no_back then
back = Base.Dir.back(b.dir)
List.filter(x -> x!=back, all_options)
else all_options

@private move_one_generic(g:Ghost.t, move_fun) =
cur_step = g.base.cur_step + 1
cur_step =
if cur_step >= g.base.max_steps then 0
else cur_step
if cur_step != 0 then {g with base = {g.base with ~cur_step}}
else
(dx, dy) = Base.Dir.deltas(g.base.dir)
pos = {
x = g.base.pos.x + dx
y = g.base.pos.y + dy
}
g = {g with base = {g.base with ~pos}}
dirs = move_fun(g.base)
dir = List.get(Random.int(List.length(dirs)), dirs) ? {down}
{g with base = {g.base with ~dir ~cur_step}}

@private move_one_dumb(ghost:Ghost.t) =
move_one_generic(ghost, build_move_options(_, true))

@private move_one_guard(ghost:Ghost.t, bp:Base.t) =
move_fun(bg) =
opts = build_move_options(bg, false)
can_see(dir) =
if bg.pos.x != bp.pos.x && bg.pos.y != bp.pos.y then false
else if bg.pos.x == bp.pos.x && bg.pos.y > bp.pos.y
&& dir == {up} then true
else if bg.pos.x == bp.pos.x && bg.pos.y < bp.pos.y
&& dir == {down} then true
else if bg.pos.y == bp.pos.y && bg.pos.x > bp.pos.x
&& dir == {left} then true
else if bg.pos.y == bp.pos.y && bg.pos.x < bp.pos.x
&& dir == {right} then true
else false
bias = List.filter(can_see, opts)
if bias == [] then
back = Base.Dir.back(bg.dir)
List.filter(x -> x!=back, opts)
else bias
move_one_generic(ghost, move_fun)

move() =
g = game.get()
ghosts = List.map(
ghost -> match ghost.ai with
| {dumb} -> move_one_dumb(ghost)
| {guard} -> move_one_guard(ghost, g.pacman.base),
g.ghosts)
game.set({g with ~ghosts})

draw(ctx:Canvas.context) =
g = game.get()
List.iter(draw_one(ctx, _), g.ghosts)

}}
131 changes: 131 additions & 0 deletions src/opacman.opa
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/* Config */

fps = 60
base_size = 32
grid_width = 26
grid_heigth = 29

/* Defaults */

default_game = {
pacman = Pacman.default
ghosts = Ghost.default
food = initial_food
score = 0
} : Game.status

/* Game */

@client game = Mutable.make(default_game)

@client draw_grid(ctx:Canvas.context) =
w = base_size
do Canvas.save(ctx)
do Canvas.set_stroke_style(ctx, {color=Color.pink})
do Canvas.set_line_width(ctx, 1.)
do Canvas.begin_path(ctx)
// lh = List.init(identity, grid_heigth)
do List.iter(
x ->
do Canvas.move_to(ctx, x*w, 1)
do Canvas.line_to(ctx, x*w, 1+w*grid_heigth)
void,
List.init(x->x+1, grid_width-1))
do List.iter(
y ->
do Canvas.move_to(ctx, 1, y*w)
do Canvas.line_to(ctx, 1+w*grid_width, y*w)
void,
List.init(y->y+1, grid_heigth-1))
do Canvas.stroke(ctx)
do Canvas.restore(ctx)
void

@client clean_frame(ctx:Canvas.context) =
Canvas.clear_rect(
ctx, 0, 0,
2+2*base_size*grid_width,
2+2*base_size*grid_heigth)

@client print_infos(g:Game.status) =
p = g.pacman
cont =
<>
Pacman at ({p.base.pos.x},{p.base.pos.y}), moving {"{p.base.dir}"}
- {Set.size(g.food)} food left
- Score: {g.score}
</>
Dom.transform([#info <- cont])

@client next_frame(ctx:Canvas.context)() =
do clean_frame(ctx)
do Pacman.move()
do Ghost.move()
do Wall.draw(ctx)
do Food.draw(ctx)
do Pacman.draw(ctx)
do Ghost.draw(ctx)
void

@client keyfun(e) =
g = game.get()
p = g.pacman
p = match (p.base.dir, e.key_code) with
// z
| ({down}, {some=122}) ->
{p with next_dir={up}
base={p.base with dir={up}
cur_step=-p.base.cur_step}}
| (_, {some=122}) -> {p with next_dir={up}}

// q
| ({right}, {some=113}) ->
{p with next_dir={left}
base={p.base with dir={left}
cur_step=-p.base.cur_step}}
| (_, {some=113}) -> {p with next_dir={left}}

// s
| ({up}, {some=115}) ->
{p with next_dir={down}
base={p.base with dir={down}
cur_step=-p.base.cur_step}}
| (_, {some=115}) -> {p with next_dir={down}}

// d
| ({left}, {some=100}) ->
{p with next_dir={right}
base={p.base with dir={left}
cur_step=-p.base.cur_step}}
| (_, {some=100}) -> {p with next_dir={right}}

// space (pause)
| (_, {some=32}) -> {p with next_dir={still}}
| _ -> p
game.set({g with pacman=p})

@client init() =
match Canvas.get(#game_holder) with
| {none} -> void
| {some=canvas} ->
ctx = Canvas.get_context_2d(canvas) |> Option.get
t = Scheduler.make_timer(1000/fps, next_frame(ctx))
_ = Dom.bind(Dom.select_document(), {keypress}, keyfun)
t.start()

body() =
<>
<canvas id="game_holder"
width="{2+base_size*grid_width}"
height="{2+base_size*grid_heigth}">
You can't see canvas, upgrade your browser !
</canvas>
<div>
<span id="info" onready={_ -> init()}></span>
</div>
</>

server = one_page_server("OPAcman", body)

css = css
canvas { border: 1px solid black; }
Loading

0 comments on commit d682679

Please sign in to comment.