diff --git a/bin/main b/bin/main index 39de58c..bef0f9a 100755 Binary files a/bin/main and b/bin/main differ diff --git a/input_test.txt b/input_test.txt index 7d9cfdd..36eba2e 100644 --- a/input_test.txt +++ b/input_test.txt @@ -1,19 +1,19 @@ 3.7 0 7 11 -0 0 0 0 0 0 0 0 0 0 0 -0 0 0 0 2 0 2 0 2 0 0 -0 0 0 2 2 2 2 2 0 0 0 -0 0 2 0 2 0 2 0 2 0 2 -3 3 3 0 0 0 2 2 4 4 0 -0 0 0 0 0 0 0 0 0 0 0 -0 0 0 2 2 2 2 3 3 2 0 +0 0 2 2 2 2 2 2 2 0 0 +0 1 2 1 2 1 2 1 2 1 0 +2 2 2 2 2 2 2 0 2 2 2 +2 1 0 1 2 1 2 1 2 1 0 +2 2 2 2 2 2 2 2 2 2 2 +0 1 2 1 2 1 2 1 0 1 0 +0 0 2 2 2 2 2 2 2 0 0 3 3 2 4 4.1 4 4 2 4.3 -0 0 3 5.5 +8 6 3 5.5 5 -2 2 0 3 2 4 5 3 +0 1 0 3 2 4 5 3 2 0 1 4 3 2 2 2 10 5 2 0 2 3 2 3 5 5 3 5 1 10 2 3 diff --git a/main.cmi b/main.cmi index 116a3ad..5e6b5e8 100644 Binary files a/main.cmi and b/main.cmi differ diff --git a/main.cmo b/main.cmo index fb2bd70..a66906e 100644 Binary files a/main.cmo and b/main.cmo differ diff --git a/main.ml b/main.ml index c446b9b..cbf17ab 100644 --- a/main.ml +++ b/main.ml @@ -3,6 +3,7 @@ let debug = false ;; let fatal_time = 1.0 ;; +let explosion_time = 1.5 ;; (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) @@ -73,28 +74,30 @@ type game_data = { mutable boosts : boost array ; } -type danger = Safe | Danger | Fatal | Blocked ;; +type danger = Safe | Danger of float | Fatal of float | Blocked ;; let int_of_danger = function | Safe -> 0 - | Danger -> 1 - | Fatal -> 2 + | Danger _ -> 1 + | Fatal _ -> 2 | Blocked -> 3 ;; -let danger_of_int = function +let danger_of_int t = function | 0 -> Safe - | 1 -> Danger - | 2 -> Fatal + | 1 -> Danger t + | 2 -> 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 ;; @@ -115,6 +118,10 @@ let print_direction = function | 4 -> Printf.printf "STILL " | _-> failwith "ERROR : invalid direction" ;; +let level_of_danger = function + | Danger k | Fatal k -> k + | _ -> 32768. ;; + (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) @@ -150,8 +157,8 @@ let print_danger_levels (map : danger array array) = match map.(l).(c) with | Blocked -> Printf.printf "@ " | Safe -> Printf.printf ". " - | Danger -> Printf.printf "! " - | Fatal -> Printf.printf "X " + | Danger _ -> Printf.printf "! " + | Fatal _ -> Printf.printf "X " done; Printf.printf "\n" done ;; @@ -251,11 +258,11 @@ let parse_input (str : string) = (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) let warn_level (b : bomb) (ct : float) = match (b.det_time -. ct) with - | k when k < fatal_time -> Fatal ; - | _ -> Danger ;; + | k when k < fatal_time -> Fatal b.det_time ; + | _ -> Danger b.det_time ;; let danger_priority (p1 : danger) (p2 : danger) = - danger_of_int (max (int_of_danger p1) (int_of_danger p2)) ;; + 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) = @@ -271,35 +278,6 @@ let evaluate_dangers (gd : game_data) = done done ; - (* add players as warning (in case they bomb) *) - for p = 0 to gd.nplayers -1 do - if p <> gd.player_id then begin - let halt = ref false in - - let bx = gd.players.(p).xy.x - and by = gd.players.(p).xy.y in - - for dir = 0 to 3 do - for w = 0 to gd.players.(p).bomb_radius 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 then begin (* crate *) - halt := true ; - res.(cx).(cy) <- danger_priority res.(cx).(cy) Danger ; - end - else begin - res.(cx).(cy) <- danger_priority res.(cx).(cy) Danger ; - end - end - done; - halt := false ; - done - end - done; - (* sort bombs based on detonation time *) for b = 0 to gd.nbombs -1 do let m = ref gd.bombs.(b).det_time @@ -349,13 +327,14 @@ let evaluate_dangers (gd : game_data) = (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) let is_safe = function - | Safe | Danger -> true - | Fatal | Blocked -> false ;; + | Safe | Danger _ -> 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 closest safe tile (or warning tile if there are no reachable 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 let lines = Array.length gd.laby and cols = Array.length gd.laby.(0) in @@ -367,73 +346,44 @@ let move_safe (gd : game_data) (dgs : danger array array) = if dgs.(cx).(cy) = Safe then raise (ReturnInt 4) ; - let q = Queue.create () in let visited = Hashtbl.create 100 in - (* 1. try not to walk on fatal tiles *) - if debug then Printf.printf "[escape] Attempt 1/3...\n"; - if cx <> 0 && is_safe dgs.(cx-1).(cy) then (* North *) Queue.add (cx-1, cy, 0) q ; - if cx <> lines -1 && is_safe dgs.(cx+1).(cy) then (* South *) Queue.add (cx+1, cy, 2) q ; - if cy <> 0 && is_safe dgs.(cx).(cy-1) then (* West *) Queue.add (cx, cy-1, 3) q ; - if cy <> cols -1 && is_safe dgs.(cx).(cy+1) then (* East *) Queue.add (cx, cy+1, 1) q ; + let has_a_safe_path (cx0 : int) (cy0 : int) (simt : float) (vid : int) = + 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 ; + try + while not (Queue.is_empty q) do + let (cx, cy, cur_t) = Queue.pop q in + if Hashtbl.find_opt visited (cx, cy, vid) <> None then () else begin + Hashtbl.add visited (cx, cy, vid) 1 ; + if dgs.(cx).(cy) = Safe then + raise (ReturnBool true) + else if dgs.(cx).(cy) = Blocked 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) q + done + end + end + done; + false + with + | ReturnBool b -> b + in - while not (Queue.is_empty q) do - let (cx, cy, dir) = Queue.pop q in - Hashtbl.add visited (cx, cy, 1) 1 ; - if dgs.(cx).(cy) = Safe then - raise (ReturnInt dir) - else begin - (* add neighbors *) - if cx <> 0 && Hashtbl.find_opt visited (cx-1, cy, 1) = None && is_safe dgs.(cx-1).(cy) then (* North *) Queue.add (cx-1, cy, dir) q ; - if cx <> lines -1 && Hashtbl.find_opt visited (cx+1, cy, 1) = None && is_safe dgs.(cx+1).(cy) then (* South *) Queue.add (cx+1, cy, dir) q ; - if cy <> 0 && Hashtbl.find_opt visited (cx, cy-1, 1) = None && is_safe dgs.(cx).(cy-1) then (* West *) Queue.add (cx, cy-1, dir) q ; - if cy <> cols -1 && Hashtbl.find_opt visited (cx, cy+1, 1) = None && is_safe dgs.(cx).(cy+1) then (* East *) Queue.add (cx, cy+1, dir) q ; - end - done; + if is_valid (cx+1) (cy) lines cols && has_a_safe_path (cx+1) (cy) gd.dt 0 then raise (ReturnInt 2) ; (* South *) + 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 *) - (* 2. find the path that get you off fatal tiles ASAP *) - if debug then Printf.printf "[escape] Attempt 2/3...\n"; - if cx <> 0 && dgs.(cx-1).(cy) <> Blocked then (* North *) Queue.add (cx-1, cy, 0) q ; - if cx <> lines -1 && dgs.(cx+1).(cy) <> Blocked then (* South *) Queue.add (cx+1, cy, 2) q ; - if cy <> 0 && dgs.(cx).(cy-1) <> Blocked then (* West *) Queue.add (cx, cy-1, 3) q ; - if cy <> cols -1 && dgs.(cx).(cy+1) <> Blocked then (* East *) Queue.add (cx, cy+1, 1) q ; - - while not (Queue.is_empty q) do - let (cx, cy, dir) = Queue.pop q in - Hashtbl.add visited (cx, cy, 2) 1 ; - if dgs.(cx).(cy) = Safe then - raise (ReturnInt dir) - else begin - (* add neighbors *) - if cx <> 0 && Hashtbl.find_opt visited (cx-1, cy, 2) = None && dgs.(cx-1).(cy) <> Blocked then (* North *) Queue.add (cx-1, cy, dir) q ; - if cx <> lines -1 && Hashtbl.find_opt visited (cx+1, cy, 2) = None && dgs.(cx+1).(cy) <> Blocked then (* South *) Queue.add (cx+1, cy, dir) q ; - if cy <> 0 && Hashtbl.find_opt visited (cx, cy-1, 2) = None && dgs.(cx-1).(cy) <> Blocked then (* West *) Queue.add (cx, cy-1, dir) q ; - if cy <> cols -1 && Hashtbl.find_opt visited (cx, cy+1, 2) = None && dgs.(cx).(cy+1) <> Blocked then (* East *) Queue.add (cx, cy+1, dir) q ; - end - done; - - (* 3. no safe tile within reach (very rare), look out for warning *) - if debug then Printf.printf "[escape] Attempt 3/3...\n"; - if cx <> 0 && dgs.(cx-1).(cy) <> Blocked then (* North *) Queue.add (cx-1, cy, 0) q ; - if cx <> lines -1 && dgs.(cx+1).(cy) <> Blocked then (* South *) Queue.add (cx+1, cy, 2) q ; - if cy <> 0 && dgs.(cx).(cy-1) <> Blocked then (* West *) Queue.add (cx, cy-1, 3) q ; - if cy <> cols -1 && dgs.(cx).(cy+1) <> Blocked then (* East *) Queue.add (cx, cy+1, 1) q ; - - while not (Queue.is_empty q) do - let (cx, cy, dir) = Queue.pop q in - Hashtbl.add visited (cx, cy, 3) 1 ; - if dgs.(cx).(cy) = Danger then - raise (ReturnInt dir) - else begin - (* add neighbors *) - if cx <> 0 && Hashtbl.find_opt visited (cx-1, cy, 3) = None && dgs.(cx-1).(cy) <> Blocked then (* North *) Queue.add (cx-1, cy, dir) q ; - if cx <> lines -1 && Hashtbl.find_opt visited (cx+1, cy, 3) = None && dgs.(cx+1).(cy) <> Blocked then (* South *) Queue.add (cx+1, cy, dir) q ; - if cy <> 0 && Hashtbl.find_opt visited (cx, cy-1, 3) = None && dgs.(cx-1).(cy) <> Blocked then (* West *) Queue.add (cx, cy-1, dir) q ; - if cy <> cols -1 && Hashtbl.find_opt visited (cx, cy+1, 3) = None && dgs.(cx).(cy+1) <> Blocked then (* East *) Queue.add (cx, cy+1, dir) q ; - end - done; - - (* 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"; 4 with @@ -445,67 +395,83 @@ let move_safe (gd : game_data) (dgs : danger array array) = 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 q = Queue.create () in + let (cx, cy) = (gd.players.(pid).xy.x, gd.players.(pid).xy.y) in + let visited = Hashtbl.create 100 in - let (cx, cy) = (gd.players.(pid).xy.x, gd.players.(pid).xy.y) in - (* O. if there's a crate right next to you, blow it up *) - if cx <> 0 && gd.laby.(cx-1).(cy) = 2 then (* North *) begin current_status := EscapeDeath; raise (ReturnInt 4) end ; - if cx <> lines -1 && gd.laby.(cx+1).(cy) = 2 then (* South *) begin current_status := EscapeDeath; raise (ReturnInt 4) end ; - if cy <> 0 && gd.laby.(cx).(cy-1) = 2 then (* West *) begin current_status := EscapeDeath; raise (ReturnInt 4) end ; - if cy <> cols -1 && gd.laby.(cx).(cy+1) = 2 then (* East *) begin current_status := EscapeDeath; raise (ReturnInt 4) end ; + let has_a_safe_path (cx0 : int) (cy0 : int) (simt : float) (vid : int) = + let q = Queue.create () in + Queue.add (cx0, cy0, simt) q ; (* everything added to q has valid coords *) + if debug then Printf.printf "[crates] Attempt %d/4...\n" vid ; + try + while not (Queue.is_empty q) do + let (cx, cy, cur_t) = Queue.pop q in + if Hashtbl.find_opt visited (cx, cy, vid) <> None then () else begin + Hashtbl.add visited (cx, cy, vid) 1 ; + if dgs.(cx).(cy) = Safe then + raise (ReturnBool true) + else if gd.laby.(cx).(cy) = 2 then (* crate *) + raise (ReturnBool true) + else if dgs.(cx).(cy) = Blocked 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) q + done + end + end + done; + false + with + | ReturnBool b -> b + in - (* 1. search without walking on dangerous tiles *) - if debug then Printf.printf "[explore] Attempt 1/2...\n"; - if cx <> 0 && is_safe dgs.(cx-1).(cy) then (* North *) Queue.add (cx-1, cy, 0) q ; - if cx <> lines -1 && is_safe dgs.(cx+1).(cy) then (* South *) Queue.add (cx+1, cy, 2) q ; - if cy <> 0 && is_safe dgs.(cx).(cy-1) then (* West *) Queue.add (cx, cy-1, 3) q ; - if cy <> cols -1 && is_safe dgs.(cx).(cy+1) then (* East *) Queue.add (cx, cy+1, 1) q ; + (* check if there's a crate next to the player *) + if is_valid (cx+1) (cy) lines cols && gd.laby.(cx+1).(cy) = 2 then begin (* Crate at South *) + current_status := EscapeDeath ; + action := 1; + if debug then Printf.printf "Fire in the hole!\n" ; + raise (ReturnInt 4) ; + end; + if is_valid (cx-1) (cy) lines cols && gd.laby.(cx-1).(cy) = 2 then begin (* Crate at North *) + current_status := EscapeDeath ; + action := 1; + if debug then Printf.printf "Fire in the hole!\n" ; + raise (ReturnInt 4) ; + end; + if is_valid (cx) (cy+1) lines cols && gd.laby.(cx).(cy+1) = 2 then begin (* Crate at East *) + current_status := EscapeDeath ; + action := 1; + if debug then Printf.printf "Fire in the hole!\n" ; + raise (ReturnInt 4) ; + end; + if is_valid (cx) (cy-1) lines cols && gd.laby.(cx).(cy-1) = 2 then begin (* Crate at West *) + current_status := EscapeDeath ; + action := 1; + if debug then Printf.printf "Fire in the hole!\n" ; + raise (ReturnInt 4) ; + end; - while not (Queue.is_empty q) do - let (cx, cy, dir) = Queue.pop q in - Hashtbl.add visited (cx, cy, 1) 1 ; - if gd.laby.(cx).(cy) = 2 then - raise (ReturnInt dir) - else begin - (* add neighbors *) - if cx <> 0 && Hashtbl.find_opt visited (cx-1, cy, 1) = None && is_safe dgs.(cx-1).(cy) then (* North *) Queue.add (cx-1, cy, dir) q ; - if cx <> lines -1 && Hashtbl.find_opt visited (cx+1, cy, 1) = None && is_safe dgs.(cx+1).(cy) then (* South *) Queue.add (cx+1, cy, dir) q ; - if cy <> 0 && Hashtbl.find_opt visited (cx, cy-1, 1) = None && is_safe dgs.(cx).(cy-1) then (* West *) Queue.add (cx, cy-1, dir) q ; - if cy <> cols -1 && Hashtbl.find_opt visited (cx, cy+1, 1) = None && is_safe dgs.(cx).(cy+1) then (* East *) Queue.add (cx, cy+1, dir) q ; - end - done; - - (* 2. search one anyway *) - if debug then Printf.printf "[explore] Attempt 2/2...\n"; - let (cx, cy) = (gd.players.(pid).xy.x, gd.players.(pid).xy.y) in - if cx <> 0 && is_safe dgs.(cx-1).(cy) then (* North *) Queue.add (cx-1, cy, 0) q ; - if cx <> lines -1 && is_safe dgs.(cx+1).(cy) then (* South *) Queue.add (cx+1, cy, 2) q ; - if cy <> 0 && is_safe dgs.(cx).(cy-1) then (* West *) Queue.add (cx, cy-1, 3) q ; - if cy <> cols -1 && is_safe dgs.(cx).(cy+1) then (* East *) Queue.add (cx, cy+1, 1) q ; - - while not (Queue.is_empty q) do - let (cx, cy, dir) = Queue.pop q in - Hashtbl.add visited (cx, cy, 2) 1 ; - if gd.laby.(cx).(cy) = 2 then - raise (ReturnInt dir) - else begin - (* add neighbors *) - if cx <> 0 && Hashtbl.find_opt visited (cx-1, cy, 2) = None && dgs.(cx-1).(cy) <> Blocked then (* North *) Queue.add (cx-1, cy, dir) q ; - if cx <> lines -1 && Hashtbl.find_opt visited (cx+1, cy, 2) = None && dgs.(cx+1).(cy) <> Blocked then (* South *) Queue.add (cx+1, cy, dir) q ; - if cy <> 0 && Hashtbl.find_opt visited (cx, cy-1, 2) = None && dgs.(cx).(cy-1) <> Blocked then (* West *) Queue.add (cx, cy-1, dir) q ; - if cy <> cols -1 && Hashtbl.find_opt visited (cx, cy+1, 2) = None && dgs.(cx).(cy+1) <> Blocked then (* East *) Queue.add (cx, cy+1, dir) q ; - end - done; + (* 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 *) + 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 "[explore] Attempt F...\n"; + if debug then Printf.printf "[crates] Attempt F...\n"; 4 with | ReturnInt k -> k ;; @@ -519,5 +485,5 @@ print_game_data game_d ;; print_danger_levels dangers ;; Printf.printf "move at ";; -print_direction (move_safe game_d dangers) ;; +print_direction (move_explore game_d dangers) ;; Printf.printf "\n" ;; \ No newline at end of file diff --git a/tp.txt b/tp.txt index adbdfa7..b73a0da 100644 --- a/tp.txt +++ b/tp.txt @@ -12,4 +12,12 @@ 0 1 2 1 2 1 2 1 2 1 2 3 3 3 0 0 0 2 2 4 4 0 0 1 0 1 0 1 0 1 0 1 0 -0 0 0 2 2 2 2 3 3 2 0 \ No newline at end of file +0 0 0 2 2 2 2 3 3 2 0 + +0 0 2 2 2 2 2 2 2 0 0 +0 1 2 1 2 1 2 1 2 1 0 +2 2 2 2 2 2 2 0 2 2 2 +2 1 0 1 2 1 2 1 2 1 0 +2 2 2 2 2 2 2 2 2 2 2 +0 1 2 1 2 1 2 1 0 1 0 +0 0 2 2 2 2 2 2 2 0 0 \ No newline at end of file