(*#! /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 players *) for p = 0 to gd.nplayers -1 do if p <> gd.player_id then begin let player_dgr = (Danger (gd.dt +. 5.5)) in for d = 0 to 3 do for o = 0 to gd.players.(p).bomb_radius do let nx = gd.players.(p).xy.x + o*(fst order.(d)) and ny = gd.players.(p).xy.y + o*(snd order.(d)) in if is_valid nx ny lines cols then res.(nx).(ny) <- danger_priority res.(nx).(ny) player_dgr ; done done end 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) = 0 then (* unclaimed tile *) res.(cx).(cy) <- res.(cx).(cy) + 1 else if gd.laby.(cx).(cy) >= 3 then (* opponent tile *) res.(cx).(cy) <- res.(cx).(cy) + 2 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 (* cell is unvisited, wont kill the player and is within range *) 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 contains_crate (gd : game_data) = Array.fold_left (fun b1 lst -> b1 || (Array.fold_left (fun b2 tile -> b2 || (tile = 2)) false lst)) false gd.laby ;; 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 exit then (* TODONE *) 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 cell_profit (d_from_p : int) (gain : int) = (100000 * gain * (1 + ln_b 7 gain)) / d_from_p ;; let move_claim (gd : game_data) (dgs : danger array array) (gns : int array array) = let lines = Array.length gd.laby and cols = Array.length gd.laby.(0) in let pid = gd.player_id in let cxi = gd.players.(pid).xy.x and cyi = gd.players.(pid).xy.y in let interval = Float.pow 0.9 (float_of_int gd.players.(pid).nspeed) in let distances = Array.make_matrix lines cols (-1) in (* 1st BFS to calculate the distances *) let visited = Hashtbl.create 100 in let q = Queue.create () in Hashtbl.add visited (cxi, cyi) 1 ; distances.(cxi).(cyi) <- 1 ; Queue.add (cxi+1, cyi, gd.dt +. interval, 2) q ; Queue.add (cxi-1, cyi, gd.dt +. interval, 2) q ; Queue.add (cxi, cyi+1, gd.dt +. interval, 2) q ; Queue.add (cxi, cyi-1, gd.dt +. interval, 2) q ; while not (Queue.is_empty q) do let (cx, cy, cur_t, dist) = Queue.pop q in if (Hashtbl.find_opt visited (cx, cy) = None && is_valid cx cy lines cols && dgs.(cx).(cy) <> Blocked && level_of_danger dgs.(cx).(cy) >= cur_t) then begin (* cell is unvisited, is not a wall, wont kill the player and is within range *) Hashtbl.add visited (cx, cy) 1 ; distances.(cx).(cy) <- dist ; for dir = 0 to 3 do let newx = cx + fst order.(dir) and newy = cy + snd order.(dir) in Queue.add (newx, newy, cur_t +. interval, dist+1) q done end done; (* compute the best value *) let maxi_gain = ref 0 in let maxi_i = ref (-1) and maxi_j = ref (-1) in Array.iteri (fun i lst -> Array.iteri ( fun j elt -> if (distances.(i).(j) <> -1) then begin maxi_gain := max (!maxi_gain) (cell_profit distances.(i).(j) gns.(i).(j)); maxi_i := i ; maxi_j := j end ) lst ) gd.laby ; (* 2nd BFS to find the direction, if you're not standing on the tile *) try if cell_profit distances.(cxi).(cyi) gns.(cxi).(cyi) = !maxi_gain then begin if gd.players.(pid).nbomb_atonce > 0 then begin (* simulate the placement of a bomb to ensure a safe escape route*) let bsize = gd.players.(pid).bomb_radius in 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 (cxi + w*(fst order.(dir)), cyi + 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 result0 = has_a_safe_path_origin_2 cxi cyi lines cols gd.dt interval gd dgs [|Safe; Bonus|] [||] [|Blocked|] [||] 80 in Hashtbl.iter (fun (k1, k2) v -> dgs.(k1).(k2) <- v) saved_dgs ; if result0 <> 4 then begin action := 1 ; current_status := EscapeDeath ; raise (ReturnInt result0) end end; raise (ReturnInt 4) end else if !maxi_i = (-1) then begin current_status := EscapeDeath ; raise (ReturnInt (has_a_safe_path_origin_2 cxi cyi lines cols gd.dt interval gd dgs [|Bonus; Safe|] [||] [|Blocked|] [||] 80)) end else begin let memo = gd.laby.(!maxi_i).(!maxi_j) in gd.laby.(!maxi_i).(!maxi_j) <- (-2) ; let result = has_a_safe_path_origin_2 cxi cyi lines cols gd.dt interval gd dgs [||] [|-2|] [|Blocked|] [||] 80 in gd.laby.(!maxi_i).(!maxi_j) <- memo ; result end with | ReturnInt k -> k ;; (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) let move_safe (gd : game_data) (dgs : danger array array) (gns : int 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, switch mode *) let (cx, cy) = (gd.players.(pid).xy.x, gd.players.(pid).xy.y) in if dgs.(cx).(cy) = Safe then begin if contains_crate gd then begin current_status := BlowUpCrates ; raise (ReturnInt (move_explore gd dgs)) ; end else begin current_status := ClaimLand ; raise (ReturnInt (move_claim gd dgs gns)) ; end 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... *) Printf.fprintf stderr "well shit\n" ; 4 end end 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 when k < 3.0 -> current_status := EscapeDeath | Danger k -> () | 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 ;; (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) (* ---------------------------------------------------------------------------------------------------------------------------------------------------- *) Printf.fprintf stderr "XXXXXXXX\n" ;; 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 gains ; 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 gains ; Printf.printf "%d " !chosen end | ClaimLand -> chosen := move_claim game_d dangers gains ; Printf.printf "%d " !chosen | 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 ;;