diff --git a/bin/main b/bin/main index a2cd4de..f5e98d6 100755 Binary files a/bin/main and b/bin/main differ diff --git a/input_test.txt b/input_test.txt index 9f6219b..7d9cfdd 100644 --- a/input_test.txt +++ b/input_test.txt @@ -1,19 +1,19 @@ 3.7 -3 +0 7 11 0 0 0 0 0 0 0 0 0 0 0 -0 1 0 1 2 1 2 1 2 1 0 +0 0 0 0 2 0 2 0 2 0 0 0 0 0 2 2 2 2 2 0 0 0 -0 1 2 1 2 1 2 1 2 1 2 +0 0 2 0 2 0 2 0 2 0 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 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 3 3 2 0 3 3 2 4 4.1 4 4 2 4.3 0 0 3 5.5 5 -0 0 0 3 2 4 5 3 +2 2 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 65809bd..dee3dc1 100644 Binary files a/main.cmi and b/main.cmi differ diff --git a/main.cmo b/main.cmo index 9e68455..4db212b 100644 Binary files a/main.cmo and b/main.cmo differ diff --git a/main.ml b/main.ml index 4ad2e2c..234bc4a 100644 --- a/main.ml +++ b/main.ml @@ -1,16 +1,26 @@ -let debug = false ;; +(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) +(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) -type bomb = { +let debug = false ;; +let fatal_time = 1.0 ;; + +(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) +(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) + +type pt = { x : int ; y : int ; +} + +type bomb = { + xy : pt ; size : int ; det_time : float ; } type player = { id : int ; - x : int ; - y : int ; + xy : pt ; nspeed : int ; nbomb_atonce : int ; bomb_radius : int ; @@ -19,22 +29,24 @@ type player = { } type boost = { - x : int ; - y : int ; + xy : pt ; spec : int ; } -let default_bomb = { +let default_point = { x = 0 ; y = 0 ; +} + +let default_bomb = { + xy = default_point ; size = 0 ; det_time = 0. ; } and default_player = { id = 0 ; - x = 0 ; - y = 0 ; + xy = default_point ; nspeed = 0 ; nbomb_atonce = 0 ; bomb_radius = 0 ; @@ -43,8 +55,7 @@ and default_player = { } and default_boost = { - x = 0 ; - y = 0 ; + xy = default_point ; spec = 0 ; } @@ -62,22 +73,46 @@ type game_data = { mutable boosts : boost array ; } -type danger = Powerup | Safe | Danger | Fatal | Blocked ;; +type danger = Safe | Danger | Fatal | Blocked ;; let int_of_danger = function | Safe -> 0 | Danger -> 1 - | Powerup -> 2 - | Fatal -> 3 - | Blocked -> 4 ;; + | Fatal -> 2 + | Blocked -> 3 ;; let danger_of_int = function | 0 -> Safe | 1 -> Danger - | 2 -> Powerup - | 3 -> Fatal + | 2 -> Fatal | _ -> Blocked ;; +type moveType = EscapeDeath | BlowUpCrates | KillPlayers | ClaimLand ;; + +exception ReturnInt of int ;; + +(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) +(* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) + +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.printf "NORTH " + | 1 -> Printf.printf "EAST " + | 2 -> Printf.printf "SOUTH " + | 3 -> Printf.printf "WEST " + | 4 -> Printf.printf "STILL " + | _-> failwith "ERROR : invalid direction" ;; + (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) @@ -95,15 +130,15 @@ let print_game_data (gd : game_data) = done ; Printf.printf "Bombs (%d) : \n" gd.nbombs ; for b = 0 to gd.nbombs -1 do - Printf.printf " [Bomb] (at %d %d) (of size %d) (blowing up at %f)\n" gd.bombs.(b).x gd.bombs.(b).y gd.bombs.(b).size gd.bombs.(b).det_time ; + Printf.printf " [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.printf "Players (%d) : \n" gd.nplayers ; for b = 0 to gd.nplayers -1 do - Printf.printf " [Player %d] (at %d %d) (holding %d %d %d %d %d)\n" gd.players.(b).id gd.players.(b).x gd.players.(b).y gd.players.(b).nspeed gd.players.(b).nbomb_atonce gd.players.(b).bomb_radius gd.players.(b).ndash gd.players.(b).ntraps ; + Printf.printf " [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.printf "Boosts (%d) : \n" gd.nboosts ; for b = 0 to gd.nboosts -1 do - Printf.printf " [Boost] (at %d %d) (of type %d)\n" gd.boosts.(b).x gd.boosts.(b).y gd.boosts.(b).spec ; + Printf.printf " [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) = @@ -115,7 +150,6 @@ let print_danger_levels (map : danger array array) = | Safe -> Printf.printf ". " | Danger -> Printf.printf "! " | Fatal -> Printf.printf "X " - | Powerup -> Printf.printf "P " done; Printf.printf "\n" done ;; @@ -179,7 +213,7 @@ let parse_input (str : string) = 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) <- {x = dat.(0) ; y = dat.(1) ; size = dat.(2) ; det_time = dtime ; + res.bombs.(b) <- {xy = {x = dat.(0) ; y = dat.(1) ;} ; size = dat.(2) ; det_time = dtime ; } done; @@ -190,7 +224,7 @@ let parse_input (str : string) = 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) ; x = dat.(0) ; y = dat.(1) ; nspeed = dat.(3) ; nbomb_atonce = dat.(4) ; bomb_radius = dat.(5) ; ndash = dat.(6) ; ntraps = dat.(7) ;} + 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 *) @@ -200,7 +234,7 @@ let parse_input (str : string) = 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) <- {x = dat.(0) ; y = dat.(1) ; spec = dat.(2)} + res.boosts.(p) <- {xy = {x = dat.(0) ; y = dat.(1) ;} ; spec = dat.(2)} done; if debug then Printf.printf "Done!\n" ; @@ -211,13 +245,17 @@ let parse_input (str : string) = 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 < 1.0 -> Fatal ; + | k when k < fatal_time -> Fatal ; | _ -> Danger ;; let danger_priority (p1 : danger) (p2 : danger) = danger_of_int (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 @@ -226,31 +264,150 @@ let evaluate_dangers (gd : game_data) = (* 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) = 1 then + if gd.laby.(l).(c) = 1 || gd.laby.(l).(c) = 2 then res.(l).(c) <- Blocked ; done done ; - (* add bomb tiles *) + (* sort bombs based on detonation time *) for b = 0 to gd.nbombs -1 do - let dgr = warn_level gd.bombs.(b) gd.dt in - for off = -gd.bombs.(b).size to gd.bombs.(b).size do - let ln = min (lines -1) (max 0 (gd.bombs.(b).x + off)) - and cl = min (cols -1) (max 0 (gd.bombs.(b).y + off)) in - if res.(ln).(gd.bombs.(b).y) = Safe then - res.(ln).(gd.bombs.(b).y) <- danger_priority (res.(ln).(gd.bombs.(b).y)) dgr ; - if res.(gd.bombs.(b).x).(cl) = Safe then - res.(gd.bombs.(b).x).(cl) <- danger_priority (res.(gd.bombs.(b).x).(cl)) dgr ; + 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 powerups *) - for p = 0 to gd.nboosts -1 do - res.(gd.boosts.(p).x).(gd.boosts.(p).y) <- Powerup ; + (* 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 is_safe = function + | 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 *) + let pid = gd.player_id in + + 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 + let q = Queue.create () in + let visited = Hashtbl.create 100 in + + (* 1. try not to walk on fatal tiles *) + if debug then Printf.printf "Attempt 1/3...\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, 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; + + (* 2. find the path that get you off fatal tiles ASAP *) + if debug then Printf.printf "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 "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 *) + if debug then Printf.printf "Attempt F...\n"; + 4 + with + | ReturnInt k -> k ;; + let game_d = parse_input "input_test.txt" ;; +let dangers = evaluate_dangers game_d ;; print_game_data game_d ;; -print_danger_levels (evaluate_dangers game_d) \ No newline at end of file +print_danger_levels dangers ;; + +Printf.printf "move at ";; +print_direction (move_safe game_d dangers) ;; +Printf.printf "\n" ;; \ No newline at end of file diff --git a/tp.txt b/tp.txt new file mode 100644 index 0000000..adbdfa7 --- /dev/null +++ b/tp.txt @@ -0,0 +1,15 @@ +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 0 0 +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 + +0 0 0 0 0 0 0 0 0 0 0 +0 1 0 1 2 1 2 1 2 1 0 +0 0 0 2 2 2 2 2 0 0 0 +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