740 lines
28 KiB
OCaml
Executable File
740 lines
28 KiB
OCaml
Executable File
(*#! /home/alexandre/.opam/myswitch/bin/ocaml *)
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
|
|
let debug_all = false ;;
|
|
let debug_data = false ;;
|
|
let fatal_time = 1.0 ;;
|
|
let explosion_time = 1.0 ;;
|
|
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
|
|
type pt = {
|
|
x : int ;
|
|
y : int ;
|
|
}
|
|
|
|
type bomb = {
|
|
xy : pt ;
|
|
size : int ;
|
|
det_time : float ;
|
|
}
|
|
|
|
type player = {
|
|
id : int ;
|
|
xy : pt ;
|
|
nspeed : int ;
|
|
nbomb_atonce : int ;
|
|
bomb_radius : int ;
|
|
ndash : int ;
|
|
ntraps : int ;
|
|
}
|
|
|
|
type boost = {
|
|
xy : pt ;
|
|
spec : int ;
|
|
}
|
|
|
|
let default_point = {
|
|
x = 0 ;
|
|
y = 0 ;
|
|
}
|
|
|
|
let default_bomb = {
|
|
xy = default_point ;
|
|
size = 0 ;
|
|
det_time = 0. ;
|
|
}
|
|
|
|
and default_player = {
|
|
id = 0 ;
|
|
xy = default_point ;
|
|
nspeed = 0 ;
|
|
nbomb_atonce = 0 ;
|
|
bomb_radius = 0 ;
|
|
ndash = 0 ;
|
|
ntraps = 0 ;
|
|
}
|
|
|
|
and default_boost = {
|
|
xy = default_point ;
|
|
spec = 0 ;
|
|
}
|
|
|
|
and useless = ref 0 ;;
|
|
|
|
type game_data = {
|
|
mutable dt : float ;
|
|
mutable player_id : int ;
|
|
mutable laby : int array array ;
|
|
mutable nbombs : int ;
|
|
mutable bombs : bomb array ;
|
|
mutable nplayers : int ;
|
|
mutable players : player array ;
|
|
mutable nboosts : int ;
|
|
mutable boosts : boost array ;
|
|
}
|
|
|
|
type danger = Safe | Danger of float | Fatal of float | Blocked | Bonus ;;
|
|
|
|
let int_of_danger = function
|
|
| Safe -> 0
|
|
| Bonus -> 1
|
|
| Danger _ -> 2
|
|
| Fatal _ -> 3
|
|
| Blocked -> 4 ;;
|
|
|
|
let danger_of_int t = function
|
|
| 0 -> Safe
|
|
| 1 -> Bonus
|
|
| 2 -> Danger t
|
|
| 3 -> Fatal t
|
|
| _ -> Blocked ;;
|
|
|
|
type moveType = EscapeDeath | BlowUpCrates | KillPlayers | ClaimLand ;;
|
|
|
|
exception ReturnInt of int ;;
|
|
exception ReturnBool of bool ;;
|
|
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
|
|
let current_status = ref BlowUpCrates ;;
|
|
let action = ref 0 ;;
|
|
|
|
let equal_pt (p1 : pt) (p2 : pt) =
|
|
p1.x = p2.x && p1.y = p2.y ;;
|
|
|
|
let swap arr i j =
|
|
let temp = arr.(i) in
|
|
arr.(i) <- arr.(j) ;
|
|
arr.(j) <- temp ;;
|
|
|
|
let is_valid i j len hei =
|
|
i >= 0 && j >= 0 && i < len && j < hei ;;
|
|
|
|
let print_direction = function
|
|
| 0 -> Printf.fprintf stderr "NORTH "
|
|
| 1 -> Printf.fprintf stderr "EAST "
|
|
| 2 -> Printf.fprintf stderr "SOUTH "
|
|
| 3 -> Printf.fprintf stderr "WEST "
|
|
| 4 -> Printf.fprintf stderr "STILL "
|
|
| _-> failwith "ERROR : invalid direction" ;;
|
|
|
|
let level_of_danger = function
|
|
| Danger k | Fatal k -> k
|
|
| _ -> 32768. ;;
|
|
|
|
let delta i j =
|
|
if i = j then 1 else 0 ;;
|
|
|
|
let overwrite_file (filename : string) =
|
|
let ptr = open_out filename in
|
|
close_out ptr ;;
|
|
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
|
|
let print_game_data (gd : game_data) =
|
|
Printf.fprintf stderr "--------------------------------| Board data |--------------------------------\n" ;
|
|
Printf.fprintf stderr "Time : %f\n" gd.dt ;
|
|
Printf.fprintf stderr "ID : %d\n" gd.player_id ;
|
|
Printf.fprintf stderr "Laby [of size %d %d]:\n" (Array.length gd.laby) (Array.length gd.laby.(0));
|
|
for l = 0 to Array.length gd.laby -1 do
|
|
Printf.fprintf stderr " " ;
|
|
for c = 0 to Array.length gd.laby.(l) -1 do
|
|
Printf.fprintf stderr "%d " gd.laby.(l).(c) ;
|
|
done;
|
|
Printf.fprintf stderr "\n"
|
|
done ;
|
|
Printf.fprintf stderr "Bombs (%d) : \n" gd.nbombs ;
|
|
for b = 0 to gd.nbombs -1 do
|
|
Printf.fprintf stderr " [Bomb] (at %d %d) (of size %d) (blowing up at %f)\n" gd.bombs.(b).xy.x gd.bombs.(b).xy.y gd.bombs.(b).size gd.bombs.(b).det_time ;
|
|
done;
|
|
Printf.fprintf stderr "Players (%d) : \n" gd.nplayers ;
|
|
for b = 0 to gd.nplayers -1 do
|
|
Printf.fprintf stderr " [Player %d] (at %d %d) (holding %d %d %d %d %d)\n" gd.players.(b).id gd.players.(b).xy.x gd.players.(b).xy.y gd.players.(b).nspeed gd.players.(b).nbomb_atonce gd.players.(b).bomb_radius gd.players.(b).ndash gd.players.(b).ntraps ;
|
|
done;
|
|
Printf.fprintf stderr "Boosts (%d) : \n" gd.nboosts ;
|
|
for b = 0 to gd.nboosts -1 do
|
|
Printf.fprintf stderr " [Boost] (at %d %d) (of type %d)\n" gd.boosts.(b).xy.x gd.boosts.(b).xy.y gd.boosts.(b).spec ;
|
|
done;;
|
|
|
|
let print_danger_levels (map : danger array array) =
|
|
Printf.fprintf stderr "--------------------------------| Danger levels |--------------------------------\n" ;
|
|
for l = 0 to (Array.length map -1) do
|
|
for c = 0 to (Array.length map.(l) -1) do
|
|
match map.(l).(c) with
|
|
| Blocked -> Printf.fprintf stderr "@ "
|
|
| Safe -> Printf.fprintf stderr ". "
|
|
| Bonus -> Printf.fprintf stderr "+ "
|
|
| Danger x -> Printf.fprintf stderr "! "
|
|
| Fatal x -> Printf.fprintf stderr "X "
|
|
done;
|
|
Printf.fprintf stderr "\n"
|
|
done ;;
|
|
|
|
let print_gain_map (map : int array array) =
|
|
Printf.fprintf stderr "--------------------------------| Gain levels |--------------------------------\n" ;
|
|
for l = 0 to (Array.length map -1) do
|
|
for c = 0 to (Array.length map.(l) -1) do
|
|
Printf.fprintf stderr "%d " map.(l).(c) ;
|
|
done;
|
|
Printf.fprintf stderr "\n"
|
|
done ;;
|
|
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
|
|
let rec ln_b b = function
|
|
| k when k < 0 -> failwith "are you sure about that ?"
|
|
| k when k < b -> 0
|
|
| k -> 1 + ln_b b (k/b) ;;
|
|
|
|
let get_meta_info (pid : int) =
|
|
let ptr = open_in ("main_"^(string_of_int pid)^".sav") in
|
|
match (int_of_string (input_line ptr)) with
|
|
| 0 -> current_status := EscapeDeath
|
|
| 1 -> current_status := BlowUpCrates
|
|
| 2 -> current_status := ClaimLand
|
|
| 3 -> current_status := KillPlayers
|
|
| _ -> current_status := EscapeDeath ;
|
|
close_in ptr ;;
|
|
|
|
let set_meta_info (pid : int) =
|
|
let ptr = open_out ("main_"^(string_of_int pid)^".sav") in
|
|
match !current_status with
|
|
| EscapeDeath -> Printf.fprintf ptr "0"
|
|
| BlowUpCrates -> Printf.fprintf ptr "1"
|
|
| ClaimLand -> Printf.fprintf ptr "2"
|
|
| KillPlayers -> Printf.fprintf ptr "3" ;
|
|
close_out ptr ;;
|
|
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
|
|
let int_of_string (str : string) =
|
|
String.fold_right (fun ch acc -> let cd = Char.code ch in if cd >= 48 || cd <= 57 then 10*acc + cd - 48 else failwith "not an integer\n") str 0 ;;
|
|
|
|
let string_of_int (k0 : int) =
|
|
String.make (1) (Char.chr (k0 + 48)) ;;
|
|
|
|
let int_n_of_string (str : string) (n : int) (nlast : int ref) =
|
|
let res = Array.make n 0 in
|
|
let rec aux idres idstr = match idstr with
|
|
| k when k = String.length str || idres >= n ->
|
|
nlast := k
|
|
| k ->
|
|
if str.[k] = ' ' then
|
|
aux (idres+1) (k+1)
|
|
else begin
|
|
let cd = Char.code str.[k] in
|
|
if cd >= 48 && cd <= 57 then begin
|
|
res.(idres) <- 10 * res.(idres) + cd - 48 ;
|
|
aux (idres) (k+1)
|
|
end
|
|
else
|
|
failwith "not an integer (n/n)\n"
|
|
end
|
|
in
|
|
aux 0 0 ;
|
|
res ;;
|
|
|
|
let parse_input (str : string) =
|
|
let ptr = open_in str in
|
|
let (res : game_data) = {dt = 0. ; player_id = 0 ; laby = [||] ; nbombs = 0 ; bombs = [||] ; nplayers = 0 ; players = [||] ; nboosts = 0 ; boosts = [||] ;} in
|
|
try
|
|
(* time *)
|
|
if debug_all then Printf.fprintf stderr "Time\n" ;
|
|
res.dt <- Float.of_string (input_line ptr) ;
|
|
|
|
(* player_id *)
|
|
if debug_all then Printf.fprintf stderr "PID\n" ;
|
|
res.player_id <- int_of_string (input_line ptr) ;
|
|
|
|
(* maze *)
|
|
if debug_all then Printf.fprintf stderr "Maze\n" ;
|
|
let msize = int_n_of_string (input_line ptr) 2 useless in
|
|
|
|
res.laby <- Array.make msize.(0) [||] ;
|
|
for lane = 0 to msize.(0) -1 do
|
|
let psd = input_line ptr in
|
|
res.laby.(lane) <- int_n_of_string psd msize.(1) useless ;
|
|
done;
|
|
|
|
(* bombs *)
|
|
if debug_all then Printf.fprintf stderr "Boom\n" ;
|
|
res.nbombs <- int_of_string (input_line ptr) ;
|
|
|
|
res.bombs <- Array.make res.nbombs default_bomb ;
|
|
for b = 0 to res.nbombs -1 do
|
|
let psd = input_line ptr
|
|
and last = ref 0 in
|
|
let dat = int_n_of_string psd 3 last in
|
|
let dtime = Float.of_string (String.init (String.length psd - !last) (fun i -> psd.[i + !last])) in
|
|
res.bombs.(b) <- {xy = {x = dat.(0) ; y = dat.(1) ;} ; size = dat.(2) ; det_time = dtime ;
|
|
}
|
|
done;
|
|
|
|
(* players *)
|
|
if debug_all then Printf.fprintf stderr "Players\n" ;
|
|
res.nplayers <- int_of_string (input_line ptr) ;
|
|
|
|
res.players <- Array.make res.nplayers default_player ;
|
|
for p = 0 to res.nplayers -1 do
|
|
let dat = int_n_of_string (input_line ptr) 8 useless in
|
|
res.players.(p) <- {id = dat.(2) ; xy = {x = dat.(0) ; y = dat.(1) ;} ; nspeed = dat.(3) ; nbomb_atonce = dat.(4) ; bomb_radius = dat.(5) ; ndash = dat.(6) ; ntraps = dat.(7) ;}
|
|
done;
|
|
|
|
(* boosts *)
|
|
if debug_all then Printf.fprintf stderr "Boosts\n" ;
|
|
res.nboosts <- int_of_string (input_line ptr) ;
|
|
|
|
res.boosts <- Array.make res.nboosts default_boost ;
|
|
for p = 0 to res.nboosts -1 do
|
|
let dat = int_n_of_string (input_line ptr) 3 useless in
|
|
res.boosts.(p) <- {xy = {x = dat.(0) ; y = dat.(1) ;} ; spec = dat.(2)}
|
|
done;
|
|
|
|
if debug_all then Printf.fprintf stderr "Done!\n" ;
|
|
close_in ptr ;
|
|
res
|
|
with
|
|
| End_of_file ->
|
|
close_in ptr ;
|
|
failwith "cannot happen unless something is wrong" ;;
|
|
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
|
|
let warn_level (b : bomb) (ct : float) = match (b.det_time -. ct) with
|
|
| k when k < fatal_time -> Fatal b.det_time ;
|
|
| _ -> Danger b.det_time ;;
|
|
|
|
let danger_priority (p1 : danger) (p2 : danger) =
|
|
danger_of_int (min (level_of_danger p1) (level_of_danger p2)) (max (int_of_danger p1) (int_of_danger p2)) ;;
|
|
|
|
let order = [|(1, 0); (-1, 0); (0, 1); (0, -1)|] ;;
|
|
let evaluate_dangers (gd : game_data) =
|
|
let lines = Array.length gd.laby
|
|
and cols = Array.length gd.laby.(0) in
|
|
let res = Array.make_matrix lines cols Safe in
|
|
|
|
(* add solid blocks *)
|
|
for l = 0 to lines -1 do
|
|
for c = 0 to cols -1 do
|
|
if gd.laby.(l).(c) = 1 || gd.laby.(l).(c) = 2 then
|
|
res.(l).(c) <- Blocked ;
|
|
done
|
|
done ;
|
|
|
|
(* add bonuses *)
|
|
for b = 0 to gd.nboosts -1 do
|
|
res.(gd.boosts.(b).xy.x).(gd.boosts.(b).xy.y) <- danger_priority (res.(gd.boosts.(b).xy.x).(gd.boosts.(b).xy.y)) Bonus
|
|
done;
|
|
|
|
(* sort bombs based on detonation time *)
|
|
for b = 0 to gd.nbombs -1 do
|
|
let m = ref gd.bombs.(b).det_time
|
|
and im = ref b in
|
|
for j = b+1 to gd.nbombs -1 do
|
|
if gd.bombs.(j).det_time < !m then begin
|
|
m := gd.bombs.(j).det_time ;
|
|
im := j ;
|
|
end
|
|
done;
|
|
swap gd.bombs b (!im) ;
|
|
done;
|
|
|
|
(* add bomb tiles *)
|
|
let exploded = Hashtbl.create 12 in
|
|
for b = 0 to gd.nbombs -1 do
|
|
let dgr = warn_level gd.bombs.(b) gd.dt in
|
|
let halt = ref false in
|
|
|
|
let bx = gd.bombs.(b).xy.x
|
|
and by = gd.bombs.(b).xy.y in
|
|
|
|
for dir = 0 to 3 do
|
|
for w = 0 to gd.bombs.(b).size do
|
|
let cx = bx + w*(fst order.(dir))
|
|
and cy = by + w*(snd order.(dir)) in
|
|
if not !halt && is_valid (cx) (cy) lines cols then begin
|
|
if gd.laby.(cx).(cy) = 1 then (* bedrock *)
|
|
halt := true ;
|
|
if gd.laby.(cx).(cy) = 2 && Hashtbl.find_opt exploded (cx, cy) = None then begin (* unexploded crate *)
|
|
halt := true ;
|
|
res.(cx).(cy) <- danger_priority res.(cx).(cy) dgr ;
|
|
Hashtbl.add exploded (cx, cy) 1
|
|
end
|
|
else begin
|
|
res.(cx).(cy) <- danger_priority res.(cx).(cy) dgr ;
|
|
end
|
|
end
|
|
done;
|
|
halt := false ;
|
|
done
|
|
done;
|
|
|
|
res ;;
|
|
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
|
|
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) || gd.laby.(ln).(cl) = 0 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 has_a_safe_path_origin (cx0 : int) (cy0 : int) (lines : int) (cols : int) (simt : float) (interval : float) (gd : game_data) (dgs : danger array array) (white_dgs : danger array) (white_gd : int array) (black_dgs : danger array) (black_gd : int array) (maxdepth : int) =
|
|
(*
|
|
core function
|
|
performs a BFS starting at (cx0, cy0), avoiding anything in blacklists and halting upon stepping on a whitelisted element
|
|
*)
|
|
let visited = Hashtbl.create 100 in
|
|
let q = Queue.create () in
|
|
|
|
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.fprintf stderr "[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.fprintf stderr "[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.fprintf stderr "[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.fprintf stderr "[escape] +West\n" ;
|
|
Queue.add (cx0, cy0-1, simt +. interval, 3) q ;
|
|
end;
|
|
|
|
if debug_all then Printf.fprintf stderr "[escape] Attempt 1/1...\n" ;
|
|
try
|
|
while not (Queue.is_empty q) do
|
|
let (cx, cy, cur_t, direct) = Queue.pop q in
|
|
if Hashtbl.find_opt visited (cx, cy) <> None then () else begin
|
|
Hashtbl.add visited (cx, cy) 1 ;
|
|
if cur_t > simt +. (float_of_int maxdepth) *. interval then (* too deep *)
|
|
raise (ReturnInt 4)
|
|
else if Array.mem dgs.(cx).(cy) white_dgs then
|
|
raise (ReturnInt direct)
|
|
else if Array.mem gd.laby.(cx).(cy) white_gd then
|
|
raise (ReturnInt direct)
|
|
else if Array.mem dgs.(cx).(cy) black_dgs then
|
|
()
|
|
else if Array.mem gd.laby.(cx).(cy) black_gd then
|
|
()
|
|
else begin (* either danger or fatal *)
|
|
let dang_time = level_of_danger dgs.(cx).(cy) in
|
|
for dir = 0 to 3 do
|
|
let newx = cx + fst order.(dir)
|
|
and newy = cy + snd order.(dir)
|
|
and newt = cur_t +. interval in
|
|
if (is_valid newx newy lines cols) && not (newt > dang_time && newt < dang_time +. explosion_time) then
|
|
Queue.add (newx, newy, newt, direct) q
|
|
done
|
|
end
|
|
end
|
|
done;
|
|
4
|
|
with
|
|
| ReturnInt b -> b ;;
|
|
|
|
let has_a_safe_path_origin_2 (cx0 : int) (cy0 : int) (lines : int) (cols : int) (simt : float) (interval : float) (gd : game_data) (dgs : danger array array) (white_dgs : danger array) (white_gd : int array) (black_dgs : danger array) (black_gd : int array) (maxdepth : int) =
|
|
(*
|
|
core function
|
|
performs a BFS starting at (cx0, cy0), avoiding anything in blacklists and halting upon stepping on a whitelisted element
|
|
*)
|
|
let visited = Hashtbl.create 100 in
|
|
let q = Queue.create () in
|
|
|
|
Hashtbl.add visited (cx0, cy0) 1 ;
|
|
|
|
|
|
Queue.add (cx0+1, cy0, simt +. interval, 2) q ;
|
|
Queue.add (cx0-1, cy0, simt +. interval, 0) q ;
|
|
Queue.add (cx0, cy0+1, simt +. interval, 1) q ;
|
|
Queue.add (cx0, cy0-1, simt +. interval, 3) q ;
|
|
|
|
if debug_all then Printf.fprintf stderr "[escape] Attempt 1/1...\n" ;
|
|
try
|
|
while not (Queue.is_empty q) do
|
|
let (cx, cy, cur_t, direct) = Queue.pop q in
|
|
|
|
if (Hashtbl.find_opt visited (cx, cy) = None && is_valid cx cy lines cols && level_of_danger dgs.(cx).(cy) >= cur_t) then begin
|
|
Hashtbl.add visited (cx, cy) 1 ;
|
|
if cur_t > simt +. (float_of_int maxdepth) *. interval then (* too deep *)
|
|
raise (ReturnInt 4)
|
|
else if Array.mem dgs.(cx).(cy) white_dgs then
|
|
raise (ReturnInt direct)
|
|
else if Array.mem gd.laby.(cx).(cy) white_gd then
|
|
raise (ReturnInt direct)
|
|
else if Array.mem dgs.(cx).(cy) black_dgs then
|
|
()
|
|
else if Array.mem gd.laby.(cx).(cy) black_gd then
|
|
()
|
|
else begin
|
|
for dir = 0 to 3 do
|
|
let newx = cx + fst order.(dir)
|
|
and newy = cy + snd order.(dir)
|
|
and newt = cur_t +. interval in
|
|
Queue.add (newx, newy, newt, direct) q
|
|
done
|
|
end
|
|
end
|
|
done;
|
|
4
|
|
with
|
|
| ReturnInt b -> b ;;
|
|
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
|
|
let is_safe = function
|
|
| Safe | Danger _ | Bonus -> true
|
|
| Fatal _ | Blocked -> false ;;
|
|
|
|
let move_safe (gd : game_data) (dgs : danger array array) =
|
|
(* use this whenever you are standing on a non-safe tile *)
|
|
(* Strat : find the shortest, safest path *)
|
|
let pid = gd.player_id in
|
|
let interval = Float.pow 0.9 (float_of_int gd.players.(pid).nspeed) in
|
|
|
|
if debug_all then Printf.fprintf stderr "I = %f\n" interval ;
|
|
|
|
let lines = Array.length gd.laby
|
|
and cols = Array.length gd.laby.(0) in
|
|
|
|
(* BFS to find the nearest safe spot (if it exists) *)
|
|
try
|
|
(* 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
|
|
if dgs.(cx).(cy) = Safe then begin
|
|
current_status := BlowUpCrates ;
|
|
raise (ReturnInt 4) ;
|
|
end;
|
|
|
|
(*let result = has_a_safe_path (cx) (cy) gd.dt in*)
|
|
let result = has_a_safe_path_origin_2 cx cy lines cols gd.dt interval gd dgs [|Bonus|] [||] [|Blocked|] [||] 20 in
|
|
|
|
if result <> 4 then result else begin
|
|
let result2 = has_a_safe_path_origin_2 cx cy lines cols gd.dt interval gd dgs [|Safe|] [||] [|Blocked|] [||] 80 in
|
|
if result2 <> 4 then result2
|
|
else begin
|
|
(* you're probably dead if the code reaches here... *)
|
|
if debug_all then Printf.fprintf stderr "[escape] Attempt F...\n";
|
|
Printf.fprintf stderr "well shit\n" ;
|
|
4
|
|
end
|
|
end
|
|
with
|
|
| ReturnInt k -> k ;;
|
|
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
|
|
let is_a_crate_nearby (gd : game_data) (dgs : danger array array) =
|
|
let pid = gd.player_id
|
|
and lines = Array.length gd.laby
|
|
and cols = Array.length gd.laby.(0) in
|
|
try
|
|
let halt = ref false in
|
|
let res = ref false in
|
|
for dir = 0 to 3 do
|
|
for o = 1 to gd.players.(pid).bomb_radius do
|
|
if not !halt then begin
|
|
let nx = gd.players.(pid).xy.x + o * (fst order.(dir))
|
|
and ny = gd.players.(pid).xy.y + o * (snd order.(dir)) in
|
|
if is_valid nx ny lines cols then begin
|
|
if gd.laby.(nx).(ny) = 2 then
|
|
res := true
|
|
else if gd.laby.(nx).(ny) = 1 then
|
|
halt := true
|
|
else if dgs.(nx).(ny) = Bonus then
|
|
raise (ReturnBool false)
|
|
end
|
|
end
|
|
done;
|
|
halt := false ;
|
|
done;
|
|
!res
|
|
with
|
|
| ReturnBool b -> b ;;
|
|
|
|
let move_explore (gd: game_data) (dgs : danger array array) =
|
|
(* destroy crates *)
|
|
let pid = gd.player_id in
|
|
let interval = Float.pow 0.9 (float_of_int gd.players.(pid).nspeed) in
|
|
|
|
let lines = Array.length gd.laby
|
|
and cols = Array.length gd.laby.(0) in
|
|
|
|
(* find nearest crate and blow it up *)
|
|
try
|
|
let (cxi, cyi) = (gd.players.(pid).xy.x, gd.players.(pid).xy.y) in
|
|
|
|
let move_with_caution (exit : bool) =
|
|
let res = has_a_safe_path_origin_2 cxi cyi lines cols gd.dt interval gd dgs [|Bonus|] [||] [|Blocked|] [||] 5 in
|
|
if res <> 4 then begin
|
|
if debug_all then Printf.fprintf stderr "[crates] success 1/2!\n" ;
|
|
res
|
|
end
|
|
else begin
|
|
let res2 = has_a_safe_path_origin_2 cxi cyi lines cols gd.dt interval gd dgs [||] [|2|] [|Blocked|] [||] 80 in
|
|
if res2 <> 4 then begin
|
|
if debug_all then Printf.fprintf stderr "[crates] success 2/2!\n" ;
|
|
res2
|
|
end
|
|
else begin
|
|
if false && exit then (* TODO *)
|
|
current_status := ClaimLand ;
|
|
Printf.fprintf stderr "Exited.\n" ;
|
|
4
|
|
end
|
|
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 (gd.dt +. 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 softlock the player, and if you can place a bomb *)
|
|
if is_a_crate_nearby gd dgs && (safe_path_with_bomb cxi cyi gd.players.(pid).bomb_radius <> 4) then begin (* Crate at South *)
|
|
if dgs.(cxi).(cyi) = Safe && gd.players.(pid).nbomb_atonce > 0 then begin
|
|
current_status := EscapeDeath ;
|
|
action := 1;
|
|
if debug_all then Printf.fprintf stderr "Fire in the hole!\n" ;
|
|
raise (ReturnInt 4) ;
|
|
end;
|
|
end;
|
|
|
|
if debug_all then Printf.fprintf stderr "[crates] Cannot bomb now, searching for a crate...\n";
|
|
|
|
(* go to one without stepping into a dangerous tile *)
|
|
raise (ReturnInt (move_with_caution true)) ;
|
|
with
|
|
| ReturnInt k -> k ;;
|
|
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
|
|
let update_strat (gd : game_data) (dgs : danger array array) = match dgs.(gd.players.(gd.player_id).xy.x).(gd.players.(gd.player_id).xy.y) with
|
|
| Safe -> ()
|
|
| Danger k -> current_status := EscapeDeath
|
|
| Bonus -> ()
|
|
| Fatal k -> (* should not happen *) current_status := EscapeDeath
|
|
| Blocked -> failwith "did you just suffocate the player ?" ;;
|
|
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
|
|
let debug_game_data1 (gd : game_data) =
|
|
Printf.fprintf stderr "[player %d started turn]\n" gd.player_id ;;
|
|
|
|
let debug_game_data (gd : game_data) =
|
|
Printf.fprintf stderr "[player %d ended turn]\n" gd.player_id ;;
|
|
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *)
|
|
|
|
let game_d = parse_input "entrees.txt" ;;
|
|
let dangers = evaluate_dangers game_d ;;
|
|
let gains = cell_values game_d ;;
|
|
|
|
get_meta_info game_d.player_id ;;
|
|
|
|
Printf.fprintf stderr "[player %d started its turn]\n" game_d.player_id ;;
|
|
|
|
if debug_data then begin
|
|
print_game_data game_d ;
|
|
print_danger_levels dangers ;
|
|
print_gain_map gains ;
|
|
end ;;
|
|
|
|
update_strat game_d dangers ;;
|
|
|
|
let chosen = ref 0 ;;
|
|
|
|
let main_actions () = match !current_status with
|
|
| EscapeDeath ->
|
|
chosen := move_safe game_d dangers ;
|
|
Printf.printf "%d " !chosen
|
|
| BlowUpCrates ->
|
|
if dangers.(game_d.players.(game_d.player_id).xy.x).(game_d.players.(game_d.player_id).xy.y) = Safe then begin
|
|
chosen := move_explore game_d dangers ;
|
|
Printf.printf "%d " !chosen
|
|
end
|
|
else begin
|
|
current_status := EscapeDeath ;
|
|
chosen := move_safe game_d dangers ;
|
|
Printf.printf "%d " !chosen
|
|
end
|
|
| ClaimLand ->
|
|
()
|
|
| KillPlayers ->
|
|
() ;;
|
|
|
|
main_actions () ;
|
|
Printf.printf "%d" !action ;
|
|
|
|
(*debug_game_data game_d ;;*)
|
|
|
|
Printf.fprintf stderr "[player %d went at direction " game_d.player_id ;;
|
|
print_direction !chosen ;;
|
|
Printf.fprintf stderr "with action %d] time at end : %f\n" !action game_d.dt ;;
|
|
|
|
set_meta_info game_d.player_id ;; |