major reworks to BFS functions

This commit is contained in:
Alexandre 2024-11-08 22:16:27 +01:00
parent e74af821f2
commit 3486ed84c1
5 changed files with 202 additions and 79 deletions

BIN
bin/main

Binary file not shown.

View File

@ -1,19 +1,19 @@
3.7 3.7
0 0
7 11 7 11
0 0 2 2 2 2 2 2 2 0 0 2 0 0 0 0 0 0 0 0 0 0
0 1 2 1 2 1 2 1 2 1 0 0 0 0 0 0 0 0 0 0 0 0
2 2 2 2 2 2 2 0 2 2 2 0 0 0 0 0 0 0 0 0 0 2
2 1 0 1 2 1 2 1 2 1 0 0 0 0 0 0 0 0 0 0 0 0
2 2 2 2 2 2 2 2 2 2 2 3 3 3 0 0 0 0 0 4 4 0
0 1 2 1 2 1 2 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 3 3 0 0
3 3
3 2 4 4.1 3 2 4 4.1
4 4 2 4.3 4 4 2 4.3
8 6 3 5.5 8 6 3 5.5
5 5
0 1 0 3 2 4 5 3 3 0 0 0 2 4 5 3
2 0 1 4 3 2 2 2 2 0 1 4 3 2 2 2
10 5 2 0 2 3 2 3 10 5 2 0 2 3 2 3
5 5 3 5 1 10 2 3 5 5 3 5 1 10 2 3

BIN
main.cmi

Binary file not shown.

BIN
main.cmo

Binary file not shown.

237
main.ml
View File

@ -1,9 +1,9 @@
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
let debug = false ;; let debug_all = true ;;
let fatal_time = 1.0 ;; let fatal_time = 1.0 ;;
let explosion_time = 1.5 ;; let explosion_time = 0.5 ;;
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
@ -122,6 +122,9 @@ let level_of_danger = function
| Danger k | Fatal k -> k | Danger k | Fatal k -> k
| _ -> 32768. ;; | _ -> 32768. ;;
let delta i j =
if i = j then 1 else 0 ;;
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
@ -163,6 +166,15 @@ let print_danger_levels (map : danger array array) =
Printf.printf "\n" Printf.printf "\n"
done ;; done ;;
let print_gain_map (map : int array array) =
Printf.printf "--------------------------------| Gain levels |--------------------------------\n" ;
for l = 0 to (Array.length map -1) do
for c = 0 to (Array.length map.(l) -1) do
Printf.printf "%d " map.(l).(c) ;
done;
Printf.printf "\n"
done ;;
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
@ -195,15 +207,15 @@ let parse_input (str : string) =
let (res : game_data) = {dt = 0. ; player_id = 0 ; laby = [||] ; nbombs = 0 ; bombs = [||] ; nplayers = 0 ; players = [||] ; nboosts = 0 ; boosts = [||] ;} in let (res : game_data) = {dt = 0. ; player_id = 0 ; laby = [||] ; nbombs = 0 ; bombs = [||] ; nplayers = 0 ; players = [||] ; nboosts = 0 ; boosts = [||] ;} in
try try
(* time *) (* time *)
if debug then Printf.printf "Time\n" ; if debug_all then Printf.printf "Time\n" ;
res.dt <- Float.of_string (input_line ptr) ; res.dt <- Float.of_string (input_line ptr) ;
(* player_id *) (* player_id *)
if debug then Printf.printf "PID\n" ; if debug_all then Printf.printf "PID\n" ;
res.player_id <- int_of_string (input_line ptr) ; res.player_id <- int_of_string (input_line ptr) ;
(* maze *) (* maze *)
if debug then Printf.printf "Maze\n" ; if debug_all then Printf.printf "Maze\n" ;
let msize = int_n_of_string (input_line ptr) 2 useless in let msize = int_n_of_string (input_line ptr) 2 useless in
res.laby <- Array.make msize.(0) [||] ; res.laby <- Array.make msize.(0) [||] ;
@ -213,7 +225,7 @@ let parse_input (str : string) =
done; done;
(* bombs *) (* bombs *)
if debug then Printf.printf "Boom\n" ; if debug_all then Printf.printf "Boom\n" ;
res.nbombs <- int_of_string (input_line ptr) ; res.nbombs <- int_of_string (input_line ptr) ;
res.bombs <- Array.make res.nbombs default_bomb ; res.bombs <- Array.make res.nbombs default_bomb ;
@ -227,7 +239,7 @@ let parse_input (str : string) =
done; done;
(* players *) (* players *)
if debug then Printf.printf "Players\n" ; if debug_all then Printf.printf "Players\n" ;
res.nplayers <- int_of_string (input_line ptr) ; res.nplayers <- int_of_string (input_line ptr) ;
res.players <- Array.make res.nplayers default_player ; res.players <- Array.make res.nplayers default_player ;
@ -237,7 +249,7 @@ let parse_input (str : string) =
done; done;
(* boosts *) (* boosts *)
if debug then Printf.printf "Boosts\n" ; if debug_all then Printf.printf "Boosts\n" ;
res.nboosts <- int_of_string (input_line ptr) ; res.nboosts <- int_of_string (input_line ptr) ;
res.boosts <- Array.make res.nboosts default_boost ; res.boosts <- Array.make res.nboosts default_boost ;
@ -246,7 +258,7 @@ let parse_input (str : string) =
res.boosts.(p) <- {xy = {x = dat.(0) ; y = dat.(1) ;} ; spec = dat.(2)} res.boosts.(p) <- {xy = {x = dat.(0) ; y = dat.(1) ;} ; spec = dat.(2)}
done; done;
if debug then Printf.printf "Done!\n" ; if debug_all then Printf.printf "Done!\n" ;
close_in ptr ; close_in ptr ;
res res
with with
@ -326,6 +338,41 @@ let evaluate_dangers (gd : game_data) =
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
let cell_values (gd : game_data) =
(* computes the net gain upon blowing up each cell *)
let lines = Array.length gd.laby
and cols = Array.length gd.laby.(0) in
let res = Array.make_matrix lines cols 0 in
let psize = gd.players.(gd.player_id).bomb_radius in
for ln = 0 to lines -1 do
for cl = 0 to cols -1 do
if gd.laby.(ln).(cl) >= 3 && gd.laby.(ln).(cl) <> 3 + gd.player_id then begin
(* use a similar method than danger for bombs *)
let halt = ref false in
for dir = 0 to 3 do
for w = 1 - delta dir 0 to psize do
let cx = ln + w*(fst order.(dir))
and cy = cl + w*(snd order.(dir)) in
if not !halt && is_valid (cx) (cy) lines cols then begin
if gd.laby.(cx).(cy) <> 1 && gd.laby.(cx).(cy) <> 2 then (* non-wall *)
res.(cx).(cy) <- res.(cx).(cy) + 1
else
halt := true ;
end
done;
halt := false ;
done
end
done
done ;
res ;;
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
let is_safe = function let is_safe = function
| Safe | Danger _ -> true | Safe | Danger _ -> true
| Fatal _ | Blocked -> false ;; | Fatal _ | Blocked -> false ;;
@ -336,6 +383,8 @@ let move_safe (gd : game_data) (dgs : danger array array) =
let pid = gd.player_id in let pid = gd.player_id in
let interval = Float.pow 0.9 (float_of_int gd.players.(pid).nspeed) in let interval = Float.pow 0.9 (float_of_int gd.players.(pid).nspeed) in
if debug_all then Printf.printf "I = %f\n" interval ;
let lines = Array.length gd.laby let lines = Array.length gd.laby
and cols = Array.length gd.laby.(0) in and cols = Array.length gd.laby.(0) in
@ -343,22 +392,44 @@ let move_safe (gd : game_data) (dgs : danger array array) =
try try
(* 0. if you're standing on a safe tile, stay there *) (* 0. if you're standing on a safe tile, stay there *)
let (cx, cy) = (gd.players.(pid).xy.x, gd.players.(pid).xy.y) in let (cx, cy) = (gd.players.(pid).xy.x, gd.players.(pid).xy.y) in
if dgs.(cx).(cy) = Safe then if dgs.(cx).(cy) = Safe then begin
current_status := BlowUpCrates ;
raise (ReturnInt 4) ; raise (ReturnInt 4) ;
end;
let visited = Hashtbl.create 100 in let visited = Hashtbl.create 100 in
let has_a_safe_path (cx0 : int) (cy0 : int) (simt : float) (vid : int) = let has_a_safe_path (cx0 : int) (cy0 : int) (simt : float) =
let q = Queue.create () in let q = Queue.create () in
Queue.add (cx0, cy0, simt) q ; (* everything added to q has valid coords *)
if debug then Printf.printf "[escape] Attempt %d/4...\n" vid ; Hashtbl.add visited (cx0, cy0) 1 ;
if is_valid (cx0+1) (cy0) lines cols && not ((simt +. interval) > (level_of_danger dgs.(cx0+1).(cy0)) && (simt +. interval) < (level_of_danger dgs.(cx0+1).(cy0)) +. explosion_time) then begin (* South *)
if debug_all then Printf.printf "[escape] +South\n" ;
Queue.add (cx0+1, cy0, simt +. interval, 2) q ;
end;
if is_valid (cx0-1) (cy0) lines cols && not ((simt +. interval) > (level_of_danger dgs.(cx0-1).(cy0)) && (simt +. interval) < (level_of_danger dgs.(cx0-1).(cy0)) +. explosion_time) then begin (* North *)
if debug_all then Printf.printf "[escape] +North\n" ;
Queue.add (cx0-1, cy0, simt +. interval, 0) q ;
end;
if is_valid (cx0) (cy0+1) lines cols && not ((simt +. interval) > (level_of_danger dgs.(cx0).(cy0+1)) && (simt +. interval) < (level_of_danger dgs.(cx0).(cy0+1)) +. explosion_time) then begin (* East *)
if debug_all then Printf.printf "[escape] +East\n" ;
Queue.add (cx0, cy0+1, simt +. interval, 1) q ;
end;
if is_valid (cx0) (cy0-1) lines cols && not ((simt +. interval) > (level_of_danger dgs.(cx0).(cy0-1)) && (simt +. interval) < (level_of_danger dgs.(cx0).(cy0-1)) +. explosion_time) then begin (* West *)
if debug_all then Printf.printf "[escape] +West\n" ;
Queue.add (cx0, cy0-1, simt +. interval, 3) q ;
end;
if debug_all then Printf.printf "[escape] Attempt 1/1...\n" ;
try try
while not (Queue.is_empty q) do while not (Queue.is_empty q) do
let (cx, cy, cur_t) = Queue.pop q in let (cx, cy, cur_t, direct) = Queue.pop q in
if Hashtbl.find_opt visited (cx, cy, vid) <> None then () else begin if Hashtbl.find_opt visited (cx, cy) <> None then () else begin
Hashtbl.add visited (cx, cy, vid) 1 ; Hashtbl.add visited (cx, cy) 1 ;
(*if debug_all then Printf.printf "dealing at (%d, %d) with dir %d\n" cx cy direct ;*)
if dgs.(cx).(cy) = Safe then if dgs.(cx).(cy) = Safe then
raise (ReturnBool true) raise (ReturnInt direct)
else if dgs.(cx).(cy) = Blocked then else if dgs.(cx).(cy) = Blocked then
() ()
else begin (* either danger or fatal *) else begin (* either danger or fatal *)
@ -368,24 +439,23 @@ let move_safe (gd : game_data) (dgs : danger array array) =
and newy = cy + snd order.(dir) and newy = cy + snd order.(dir)
and newt = cur_t +. interval in and newt = cur_t +. interval in
if (is_valid newx newy lines cols) && not (newt > dang_time && newt < dang_time +. explosion_time) then if (is_valid newx newy lines cols) && not (newt > dang_time && newt < dang_time +. explosion_time) then
Queue.add (newx, newy, newt) q Queue.add (newx, newy, newt, direct) q
done done
end end
end end
done; done;
false 4
with with
| ReturnBool b -> b | ReturnInt b -> b
in in
if is_valid (cx+1) (cy) lines cols && has_a_safe_path (cx+1) (cy) gd.dt 0 then raise (ReturnInt 2) ; (* South *) let result = has_a_safe_path (cx) (cy) gd.dt in
if is_valid (cx-1) (cy) lines cols && has_a_safe_path (cx-1) (cy) gd.dt 1 then raise (ReturnInt 0) ; (* North *)
if is_valid (cx) (cy+1) lines cols && has_a_safe_path (cx) (cy+1) gd.dt 2 then raise (ReturnInt 1) ; (* East *)
if is_valid (cx) (cy-1) lines cols && has_a_safe_path (cx) (cy-1) gd.dt 3 then raise (ReturnInt 3) ; (* West *)
if result <> 4 then result else begin
(* you're probably dead if the code reaches here... *) (* you're probably dead if the code reaches here... *)
if debug then Printf.printf "[escape] Attempt F...\n"; if debug_all then Printf.printf "[escape] Attempt F...\n";
4 4
end
with with
| ReturnInt k -> k ;; | ReturnInt k -> k ;;
@ -402,23 +472,40 @@ let move_explore (gd: game_data) (dgs : danger array array) =
(* find nearest crate and blow it up *) (* find nearest crate and blow it up *)
try try
let (cx, cy) = (gd.players.(pid).xy.x, gd.players.(pid).xy.y) in let (cxi, cyi) = (gd.players.(pid).xy.x, gd.players.(pid).xy.y) in
let visited = Hashtbl.create 100 in let visited = Hashtbl.create 100 in
let has_a_safe_path (cx0 : int) (cy0 : int) (simt : float) (vid : int) = let has_a_safe_path (cx0 : int) (cy0 : int) (simt : float) =
let q = Queue.create () in let q = Queue.create () in
Queue.add (cx0, cy0, simt) q ; (* everything added to q has valid coords *) Hashtbl.add visited (cx0, cy0) 1 ;
if debug then Printf.printf "[crates] Attempt %d/4...\n" vid ;
if is_valid (cx0+1) (cy0) lines cols && not ((simt +. interval) > (level_of_danger dgs.(cx0+1).(cy0)) && (simt +. interval) < (level_of_danger dgs.(cx0+1).(cy0)) +. explosion_time) then begin (* South *)
if debug_all then Printf.printf "[escape] +South\n" ;
Queue.add (cx0+1, cy0, simt +. interval, 2) q ;
end;
if is_valid (cx0-1) (cy0) lines cols && not ((simt +. interval) > (level_of_danger dgs.(cx0-1).(cy0)) && (simt +. interval) < (level_of_danger dgs.(cx0-1).(cy0)) +. explosion_time) then begin (* North *)
if debug_all then Printf.printf "[escape] +North\n" ;
Queue.add (cx0-1, cy0, simt +. interval, 0) q ;
end;
if is_valid (cx0) (cy0+1) lines cols && not ((simt +. interval) > (level_of_danger dgs.(cx0).(cy0+1)) && (simt +. interval) < (level_of_danger dgs.(cx0).(cy0+1)) +. explosion_time) then begin (* East *)
if debug_all then Printf.printf "[escape] +East\n" ;
Queue.add (cx0, cy0+1, simt +. interval, 1) q ;
end;
if is_valid (cx0) (cy0-1) lines cols && not ((simt +. interval) > (level_of_danger dgs.(cx0).(cy0-1)) && (simt +. interval) < (level_of_danger dgs.(cx0).(cy0-1)) +. explosion_time) then begin (* West *)
if debug_all then Printf.printf "[escape] +West\n" ;
Queue.add (cx0, cy0-1, simt +. interval, 3) q ;
end;
if debug_all then Printf.printf "[crates] Attempt 1/1...\n" ;
try try
while not (Queue.is_empty q) do while not (Queue.is_empty q) do
let (cx, cy, cur_t) = Queue.pop q in let (cx, cy, cur_t, direct) = Queue.pop q in
if Hashtbl.find_opt visited (cx, cy, vid) <> None then () else begin if Hashtbl.find_opt visited (cx, cy) <> None then () else begin
Hashtbl.add visited (cx, cy, vid) 1 ; Hashtbl.add visited (cx, cy) 1 ;
if dgs.(cx).(cy) = Safe then (*if debug_all then Printf.printf "[crates] exploring (%d, %d) at time %f for dir %d\n" cx cy cur_t direct ;*)
raise (ReturnBool true) if gd.laby.(cx).(cy) = 2 then (* crate *)
else if gd.laby.(cx).(cy) = 2 then (* crate *) raise (ReturnInt direct)
raise (ReturnBool true)
else if dgs.(cx).(cy) = Blocked then else if dgs.(cx).(cy) = Blocked then
() ()
else begin (* either danger or fatal *) else begin (* either danger or fatal *)
@ -428,51 +515,84 @@ let move_explore (gd: game_data) (dgs : danger array array) =
and newy = cy + snd order.(dir) and newy = cy + snd order.(dir)
and newt = cur_t +. interval in and newt = cur_t +. interval in
if (is_valid newx newy lines cols) && not (newt > dang_time && newt < dang_time +. explosion_time) then if (is_valid newx newy lines cols) && not (newt > dang_time && newt < dang_time +. explosion_time) then
Queue.add (newx, newy, newt) q Queue.add (newx, newy, newt, direct) q
done done
end end
end end
done; done;
false (-1)
with with
| ReturnBool b -> b | ReturnInt k -> k
in in
(* check if there's a crate next to the player *) let move_with_caution (exit : bool) =
if is_valid (cx+1) (cy) lines cols && gd.laby.(cx+1).(cy) = 2 then begin (* Crate at South *) let res = has_a_safe_path cxi cyi gd.dt in
if res <> -1 then begin
if debug_all then Printf.printf "[crates] success!\n" ;
res
end
else begin
if exit then
current_status := ClaimLand ;
4
end
in
let safe_path_with_bomb (bx : int) (by : int) (bsize : int) =
(* simulate the placement of a bomb, and test if that stills allows safe escape *)
let bomb_hash = Hashtbl.create (4 * (bsize +1)) in
let saved_dgs = Hashtbl.create (4 * (bsize +1)) in
for dir = 0 to 3 do
for w = 0 to bsize do
Hashtbl.add bomb_hash (bx + w*(fst order.(dir)), by + w*(snd order.(dir))) (Danger 5.5) ;
done
done;
Hashtbl.iter
(fun (kx, ky) v ->
if is_valid kx ky lines cols then begin
Hashtbl.add saved_dgs (kx, ky) dgs.(kx).(ky) ;
dgs.(kx).(ky) <- danger_priority dgs.(kx).(ky) v
end
)
bomb_hash ;
let result = move_with_caution false in
Hashtbl.iter (fun (k1, k2) v -> dgs.(k1).(k2) <- v) saved_dgs ;
result
in
(* check if there's a crate next to the player, and if upon placing a bomb it won't block the player *)
if is_valid (cxi+1) (cyi) lines cols && gd.laby.(cxi+1).(cyi) = 2 && (safe_path_with_bomb cxi cyi gd.players.(pid).bomb_radius <> 4) then begin (* Crate at South *)
current_status := EscapeDeath ; current_status := EscapeDeath ;
action := 1; action := 1;
if debug then Printf.printf "Fire in the hole!\n" ; if debug_all then Printf.printf "Fire in the hole!\n" ;
raise (ReturnInt 4) ; raise (ReturnInt 4) ;
end; end;
if is_valid (cx-1) (cy) lines cols && gd.laby.(cx-1).(cy) = 2 then begin (* Crate at North *)
if is_valid (cxi-1) (cyi) lines cols && gd.laby.(cxi-1).(cyi) = 2 && (safe_path_with_bomb cxi cyi gd.players.(pid).bomb_radius <> 4) then begin (* Crate at North *)
current_status := EscapeDeath ; current_status := EscapeDeath ;
action := 1; action := 1;
if debug then Printf.printf "Fire in the hole!\n" ; if debug_all then Printf.printf "Fire in the hole!\n" ;
raise (ReturnInt 4) ; raise (ReturnInt 4) ;
end; end;
if is_valid (cx) (cy+1) lines cols && gd.laby.(cx).(cy+1) = 2 then begin (* Crate at East *)
if is_valid (cxi) (cyi+1) lines cols && gd.laby.(cxi).(cyi+1) = 2 && (safe_path_with_bomb cxi cyi gd.players.(pid).bomb_radius <> 4) then begin (* Crate at East *)
current_status := EscapeDeath ; current_status := EscapeDeath ;
action := 1; action := 1;
if debug then Printf.printf "Fire in the hole!\n" ; if debug_all then Printf.printf "Fire in the hole!\n" ;
raise (ReturnInt 4) ; raise (ReturnInt 4) ;
end; end;
if is_valid (cx) (cy-1) lines cols && gd.laby.(cx).(cy-1) = 2 then begin (* Crate at West *)
if is_valid (cxi) (cyi-1) lines cols && gd.laby.(cxi).(cyi-1) = 2 && (safe_path_with_bomb cxi cyi gd.players.(pid).bomb_radius <> 4) then begin (* Crate at West *)
current_status := EscapeDeath ; current_status := EscapeDeath ;
action := 1; action := 1;
if debug then Printf.printf "Fire in the hole!\n" ; if debug_all then Printf.printf "Fire in the hole!\n" ;
raise (ReturnInt 4) ; raise (ReturnInt 4) ;
end; end;
if debug_all then Printf.printf "[crates] Cannot bomb now, searching for a crate...\n";
(* go to one without stepping into a dangerous tile *) (* go to one without stepping into a dangerous tile *)
if is_valid (cx+1) (cy) lines cols && has_a_safe_path (cx+1) (cy) gd.dt 0 then raise (ReturnInt 2) ; (* South *) raise (ReturnInt (move_with_caution true)) ;
if is_valid (cx-1) (cy) lines cols && has_a_safe_path (cx-1) (cy) gd.dt 1 then raise (ReturnInt 0) ; (* North *)
if is_valid (cx) (cy+1) lines cols && has_a_safe_path (cx) (cy+1) gd.dt 2 then raise (ReturnInt 1) ; (* East *)
if is_valid (cx) (cy-1) lines cols && has_a_safe_path (cx) (cy-1) gd.dt 3 then raise (ReturnInt 3) ; (* West *)
(* no crates ? Go for kills *)
if debug then Printf.printf "[crates] Attempt F...\n";
4
with with
| ReturnInt k -> k ;; | ReturnInt k -> k ;;
@ -481,9 +601,12 @@ let move_explore (gd: game_data) (dgs : danger array array) =
let game_d = parse_input "input_test.txt" ;; let game_d = parse_input "input_test.txt" ;;
let dangers = evaluate_dangers game_d ;; let dangers = evaluate_dangers game_d ;;
let gains = cell_values game_d ;;
print_game_data game_d ;; print_game_data game_d ;;
print_danger_levels dangers ;; print_danger_levels dangers ;;
print_gain_map gains ;;
Printf.printf "move at ";; Printf.printf "move at\n";;
print_direction (move_explore game_d dangers) ;; print_direction (move_explore game_d dangers) ;;
Printf.printf "\n" ;; Printf.printf "\n" ;;