module Problem const WIDTH = 25 const HEIGHT = 60 const MAX_TURN = 1000 const TIME_LIMIT = 1.65 const MIN_PERCENT = 1 const MAX_PERCENT = 8 const INF = 1 << 30 mutable struct Local row::Int inputs::Vector{String} end function Local() inputs = readlines() Local(2, inputs) end struct Server end readint(::Server) = parse(Int, readline()) readints(::Server) = parse.(Int, readline() |> split) function readint(l::Local) n = parse(Int, l.inputs[l.row]) l.row += 1 n end function readints(l::Local) nums = parse.(Int, split(l.inputs[l.row])) l.row += 1 nums end mutable struct Player x::Int level::Int breaking_power_total::Int score::Int end function Player() Player(12 + 1, 1, 0, 0) end mutable struct Enemy init_hp::Int now_hp::Int power::Int x::Int y::Int end function Enemy(init_hp, power, x0) Enemy(init_hp, init_hp, power, x0 + 1, HEIGHT) end Base.copy(e::Enemy) = Enemy(e.init_hp, e.now_hp, e.power, e.x, e.y) mutable struct AppearanceCounter appear_count::Int no_appear_count::Int percent::Float64 end function AppearanceCounter() AppearanceCounter(0, 0, 5.0) end function percentage(counter::AppearanceCounter) p = counter.appear_count / (counter.appear_count + counter.no_appear_count) * 100 p = max(MIN_PERCENT, p) p = min(MAX_PERCENT, p) p end function preprocess!(enemies::Vector{Vector{Enemy}}, counters::Vector{AppearanceCounter}, new_enemies) counnts = [0 for _ in 1:WIDTH] for i in 1:WIDTH for j in eachindex(enemies[i]) enemies[i][j].y -= 1 end if !isempty(enemies[i]) && enemies[i][begin].y == 0 popfirst!(enemies[i]) end end for enemy in new_enemies push!(enemies[enemy.x], enemy) counnts[enemy.x] += 1 end for i in 1:WIDTH if counnts[i] > 0 counters[i].appear_count += 1 else counters[i].no_appear_count += 1 end end end function postprocess!(enemies::Vector{Vector{Enemy}}, player::Player) if !isempty(enemies[player.x]) enemies[player.x][begin].now_hp -= player.level if enemies[player.x][begin].now_hp <= 0 player.score += enemies[player.x][begin].init_hp player.breaking_power_total += enemies[player.x][begin].power player.level = 1 + player.breaking_power_total ÷ 100 popfirst!(enemies[player.x]) end end end function diff(a, b) (a - b)^2 end function update_percentages!(counters::Vector{AppearanceCounter}, probabilities, turn) t = turn / MAX_TURN for i in 1:WIDTH old_p = probabilities[i] expected_p = percentage(counters[i]) probabilities[i] = old_p probabilities[i] += old_p * (1 - t) + expected_p * t end end const TARGET_LEVEL = 250 function enemy_evaluation1(enemy::Enemy, turn, player::Player) t = min(1, player.level / TARGET_LEVEL) value = enemy.power * (1 - t) + enemy.init_hp * t value end function approach_turn_simple(player::Player, enemies::Vector{Vector{Enemy}}, target_x) turn = 0 now = player.x # enemies_ys = [Int[] for _ in 1:WIDTH] # for i in 1:WIDTH # for enemy in enemies[i] # push!(enemies_ys[i], enemy.y) # end # end copy_enemies = [Enemy[] for i in 1:WIDTH] for i in 1:WIDTH for enemy in enemies[i] if enemy.y < WIDTH ÷ 2 + 2 push!(copy_enemies[i], copy(enemy)) end end end copy_player = deepcopy(player) earn_value = 0 while now != target_x dirs = if mod(now - target_x, WIDTH) < mod(target_x - now, WIDTH) [-1, 0, 1] elseif mod(now - target_x, WIDTH) > mod(target_x - now, WIDTH) [1, 0, -1] else break end can_move = false for dir in dirs next = now + dir if next < 1 next += WIDTH elseif next > WIDTH next -= WIDTH end # while !isempty(enemies_ys[next]) && enemies_ys[next][begin] < turn + 1 # popfirst!(enemies_ys[next]) # end # if isempty(enemies_ys[next]) # now = next # turn += 1 # can_move = true # break # else # enemy_y = enemies_ys[next][begin] # if enemy_y != turn + 1 # now = next # turn += 1 # can_move = true # break # end # end while !isempty(copy_enemies[next]) && copy_enemies[next][begin].y < turn + 1 popfirst!(copy_enemies[next]) end if isempty(copy_enemies[next]) now = next turn += 1 can_move = true break else enemy_y = copy_enemies[next][begin].y if enemy_y != turn + 1 now = next turn += 1 can_move = true break end end end if !can_move return copy_player, earn_value, INF end postprocess!(copy_enemies, copy_player) end # println(stderr, "earnscore: $earn_value, turn: $turn") copy_player, earn_value, turn end function expected_turn_for_braking(player::Player, enemies::Vector{Vector{Enemy}}, target_x) turn = 0 copy_player, earn_value, approach_turn = approach_turn_simple(player, enemies, target_x) if approach_turn == INF return copy_player, earn_value, INF end turn = approach_turn # # 移動して攻撃も移動せず攻撃も使うターンは変わらない if turn > 0 turn -= 1 end braking_turn = (enemies[target_x][begin].now_hp + (player.level - 1)) ÷ player.level turn += braking_turn # yが以下の場合は壊せない経過ターン # よくわからないけどバグ対策で1引いた if turn >= enemies[target_x][begin].y - 1 turn = INF end copy_player, earn_value, turn end function main(env) player = Player() enemies = [Enemy[] for _ in 1:WIDTH] counters = [AppearanceCounter() for _ in 1:WIDTH] probabilities = [4.5 for _ in 1:WIDTH] for turn in 1:MAX_TURN new_enemies = input(env) preprocess!(enemies, counters, new_enemies) update_percentages!(counters, probabilities, turn) cand_enemies = Enemy[] for i in 1:WIDTH if !isempty(enemies[i]) push!(cand_enemies, enemies[i][begin]) end end enemies_length = [length(enemies[i]) for i in 1:WIDTH] enemies_init_hp_sum = [sum([enemy.init_hp for enemy in enemies[i]]) for i in 1:WIDTH] enemies_score_sum = deepcopy(enemies_init_hp_sum) enemies_num_sum = deepcopy(enemies_length) for i in 1:WIDTH for j in 1:2 enemies_score_sum[i] += enemies_init_hp_sum[mod1(i - j, WIDTH)] ÷ (j + 1) enemies_score_sum[i] += enemies_init_hp_sum[mod1(i + j, WIDTH)] ÷ (j + 1) enemies_num_sum[i] += enemies_length[mod1(i - j, WIDTH)] ÷ (j + 1) enemies_num_sum[i] += enemies_length[mod1(i + j, WIDTH)] ÷ (j + 1) end end target_x = -1 best_value = -1 target_enemy = Enemy(0, 0, 0) best_used_turn = INF for enemy in cand_enemies x = enemy.x copy_player, add_earn_value, use_turn = expected_turn_for_braking(player, enemies, x) earn_value = enemy_evaluation1(enemy, turn, copy_player) earn_value += add_earn_value if use_turn == INF continue end if use_turn + turn > MAX_TURN continue end value = earn_value / use_turn + enemies_score_sum[x] * 0.1 * min(1, copy_player.level / TARGET_LEVEL) # println(stderr, "x: $x, value: $value, score_sum: $(enemies_score_sum[x]), num_sum: $(enemies_num_sum[x]), use_turn: $use_turn, turn: $turn, earn_value: $earn_value, add_earn_value: $add_earn_value") if best_value < value best_value = value target_x = x target_enemy = enemy best_used_turn = use_turn end end dirs = if mod(player.x - target_x, WIDTH) < mod(target_x - player.x, WIDTH) ["L", "S", "R"] elseif mod(player.x - target_x, WIDTH) > mod(target_x - player.x, WIDTH) ["R", "S", "L"] else ["S", "L", "R"] end for dir in dirs now = player.x if dir == "L" now -= 1 if now < 1 now += WIDTH end elseif dir == "R" now += 1 if now > WIDTH now -= WIDTH end end if !isempty(enemies[now]) && enemies[now][begin].y <= 2 continue else println(dir) player.x = now break end end # println("# target_x: ", target_x) # println("# best_value: ", best_value) # println("# player: ", player.level, " ", player.x) # println("# enemy: ", target_enemy.now_hp, " ", target_enemy.x) # println("# best_used_turn: ", best_used_turn) # println("# dirs: ", dirs) postprocess!(enemies, player) end println(stderr, "Score: ", player.score) end function input(env) n = readint(env) if n == -1 exit() end new_enemies = [Enemy(readints(env)...) for _ in 1:n] new_enemies end end #region run probblem if abspath(PROGRAM_FILE) == @__FILE__ env = Problem.Server() Problem.main(env) else tests_path = joinpath(@__DIR__, "in") file_name = "0000.txt" sample_file = joinpath(tests_path, file_name) out_path = joinpath(@__DIR__, "out") out_file = joinpath(out_path, file_name) redirect_stdio(stdin=sample_file, stdout=out_file) do env = Problem.Local() # @profview Problem.main(env) Problem.main(env) end end #endregion