forked from HenJi/OPAcman
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit d682679
Showing
8 changed files
with
512 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 **/#*# |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
|
||
}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
||
}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
|
||
}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } |
Oops, something went wrong.