diff --git a/bin/main b/bin/main index bef0f9a..ca9926e 100755 Binary files a/bin/main and b/bin/main differ diff --git a/input_test.txt b/input_test.txt index 36eba2e..20bb71f 100644 --- a/input_test.txt +++ b/input_test.txt @@ -1,19 +1,19 @@ 3.7 0 7 11 -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 +2 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 2 +0 0 0 0 0 0 0 0 0 0 0 +3 3 3 0 0 0 0 0 4 4 0 +0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 3 3 0 0 3 3 2 4 4.1 4 4 2 4.3 8 6 3 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 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 5e6b5e8..15e807a 100644 Binary files a/main.cmi and b/main.cmi differ diff --git a/main.cmo b/main.cmo index a66906e..6e4f181 100644 Binary files a/main.cmo and b/main.cmo differ diff --git a/main.ml b/main.ml index cbf17ab..a633b7d 100644 --- a/main.ml +++ b/main.ml @@ -1,9 +1,9 @@ (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) -let debug = false ;; +let debug_all = true ;; 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 | _ -> 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" 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 try (* time *) - if debug then Printf.printf "Time\n" ; + if debug_all then Printf.printf "Time\n" ; res.dt <- Float.of_string (input_line ptr) ; (* 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) ; (* 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 res.laby <- Array.make msize.(0) [||] ; @@ -213,7 +225,7 @@ let parse_input (str : string) = done; (* 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.bombs <- Array.make res.nbombs default_bomb ; @@ -227,7 +239,7 @@ let parse_input (str : string) = done; (* 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.players <- Array.make res.nplayers default_player ; @@ -237,7 +249,7 @@ let parse_input (str : string) = done; (* 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.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)} done; - if debug then Printf.printf "Done!\n" ; + if debug_all then Printf.printf "Done!\n" ; close_in ptr ; res 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 | Safe | Danger _ -> true | Fatal _ | Blocked -> false ;; @@ -336,6 +383,8 @@ let move_safe (gd : game_data) (dgs : danger array array) = 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.printf "I = %f\n" interval ; + let lines = Array.length gd.laby and cols = Array.length gd.laby.(0) in @@ -343,22 +392,44 @@ let move_safe (gd : game_data) (dgs : danger array array) = 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 + if dgs.(cx).(cy) = Safe then begin + current_status := BlowUpCrates ; raise (ReturnInt 4) ; + end; 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 - 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 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 ; + 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 debug_all then Printf.printf "dealing at (%d, %d) with dir %d\n" cx cy direct ;*) if dgs.(cx).(cy) = Safe then - raise (ReturnBool true) + raise (ReturnInt direct) else if dgs.(cx).(cy) = Blocked then () 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 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 + Queue.add (newx, newy, newt, direct) q done end end done; - false + 4 with - | ReturnBool b -> b + | ReturnInt b -> b in - 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 *) + let result = has_a_safe_path (cx) (cy) gd.dt in - (* you're probably dead if the code reaches here... *) - if debug then Printf.printf "[escape] Attempt F...\n"; - 4 + if result <> 4 then result else begin + (* you're probably dead if the code reaches here... *) + if debug_all then Printf.printf "[escape] Attempt F...\n"; + 4 + end with | ReturnInt k -> k ;; @@ -402,23 +472,40 @@ let move_explore (gd: game_data) (dgs : danger array array) = (* find nearest crate and blow it up *) 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 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 - Queue.add (cx0, cy0, simt) q ; (* everything added to q has valid coords *) - if debug then Printf.printf "[crates] 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 "[crates] Attempt 1/1...\n" ; 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) + 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 debug_all then Printf.printf "[crates] exploring (%d, %d) at time %f for dir %d\n" cx cy cur_t direct ;*) + if gd.laby.(cx).(cy) = 2 then (* crate *) + raise (ReturnInt direct) else if dgs.(cx).(cy) = Blocked then () 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 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 + Queue.add (newx, newy, newt, direct) q done end end done; - false + (-1) with - | ReturnBool b -> b + | ReturnInt k -> k in - (* 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 *) + let move_with_caution (exit : bool) = + 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 ; 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" ; + if debug_all then Printf.printf "Fire in the hole!\n" ; raise (ReturnInt 4) ; end; + 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 ; + action := 1; + if debug_all then Printf.printf "Fire in the hole!\n" ; + raise (ReturnInt 4) ; + end; + + 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 ; + action := 1; + if debug_all then Printf.printf "Fire in the hole!\n" ; + raise (ReturnInt 4) ; + end; + + 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 ; + action := 1; + if debug_all then Printf.printf "Fire in the hole!\n" ; + raise (ReturnInt 4) ; + 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 *) - 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 "[crates] Attempt F...\n"; - 4 + raise (ReturnInt (move_with_caution true)) ; with | 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 dangers = evaluate_dangers game_d ;; +let gains = cell_values game_d ;; + print_game_data game_d ;; print_danger_levels dangers ;; +print_gain_map gains ;; -Printf.printf "move at ";; +Printf.printf "move at\n";; print_direction (move_explore game_d dangers) ;; Printf.printf "\n" ;; \ No newline at end of file