Created
April 26, 2016 18:33
-
-
Save eigenein/8ea84e6c51f148355b6f9a50fc896068 to your computer and use it in GitHub Desktop.
CodeRunner 2015
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# coding: utf-8 | |
from collections import deque, namedtuple | |
from math import pi, copysign, cos, sin | |
from typing import Tuple | |
from model.Car import Car | |
from model.CarType import CarType | |
from model.Game import Game | |
from model.Move import Move | |
from model.TileType import TileType | |
from model.World import World | |
# Manually handle special cases. | |
PREDEFINED_TRACKS = { | |
"map03": [ | |
(2, 5), (1, 5), (1, 4), (0, 4), (0, 3), (0, 2), (1, 2), (1, 1), (2, 1), (2, 0), (3, 0), (4, 0), (4, 1), (5, 1), | |
(5, 2), (6, 2), (6, 3), (6, 4), (6, 5), (6, 6), (6, 7), (5, 7), (4, 7), (3, 7), (2, 7), (2, 6), | |
], | |
"map06": [ | |
(12, 15), (11, 15), (10, 15), (9, 15), (8, 15), (7, 15), (6, 15), (5, 15), (4, 15), (3, 15), (2, 15), (1, 15), | |
(0, 15), (0, 14), (0, 13), (0, 12), (0, 11), (0, 10), (0, 9), (0, 8), (0, 7), (0, 6), (0, 5), (0, 4), (0, 3), | |
(0, 2), (0, 1), (0, 0), (1, 0), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9), | |
(2, 10), (2, 11), (2, 12), (2, 13), (2, 14), (3, 14), (4, 14), (5, 14), (6, 14), (6, 13), (7, 13), (7, 12), | |
(8, 12), (9, 12), (9, 13), (10, 13), (11, 13), (12, 13), (13, 13), (13, 12), (14, 12), (15, 12), (15, 13), | |
(15, 14), (14, 14), (14, 15), (13, 15), | |
], | |
"map14": [ | |
(0, 2), (0, 3), (1, 3), (1, 4), (2, 4), (3, 4), (3, 5), (4, 5), (4, 6), (4, 7), (5, 7), (5, 8), (6, 8), (7, 8), | |
(7, 7), (8, 7), (8, 6), (8, 5), (8, 4), (8, 3), (8, 2), (8, 1), (9, 1), (9, 0), (10, 0), (11, 0), (11, 1), | |
(12, 1), (12, 2), (12, 3), (11, 3), (11, 4), (10, 4), (9, 4), (8, 4), (7, 4), (6, 4), (5, 4), (5, 5), (4, 5), | |
(4, 6), (4, 7), (5, 7), (5, 8), (6, 8), (7, 8), (7, 9), (8, 9), (8, 10), (9, 10), (9, 11), (10, 11), (10, 12), | |
(11, 12), (12, 12), (12, 11), (12, 10), (12, 9), (11, 9), (11, 8), (10, 8), (9, 8), (9, 7), (8, 7), (8, 6), | |
(8, 5), (7, 5), (7, 4), (6, 4), (5, 4), (5, 5), (4, 5), (4, 6), (4, 7), (4, 8), (4, 9), (4, 10), (4, 11), | |
(3, 11), (3, 12), (2, 12), (1, 12), (1, 11), (0, 11), (0, 10), (0, 9), (1, 9), (1, 8), (2, 8), (3, 8), (4, 8), | |
(5, 8), (6, 8), (7, 8), (7, 7), (8, 7), (8, 6), (8, 5), (7, 5), (7, 4), (6, 4), (5, 4), (5, 3), (4, 3), (4, 2), | |
(3, 2), (3, 1), (2, 1), (2, 0), (1, 0), (0, 0), (0, 1), | |
], | |
"_ud1": [ | |
(10, 15), (9, 15), (8, 15), (7, 15), (7, 14), (6, 14), (6, 13), (5, 13), (5, 12), (5, 11), (4, 11), (3, 11), | |
(3, 10), (2, 10), (2, 9), (2, 8), (2, 7), (1, 7), (1, 6), (0, 6), (0, 5), (0, 4), (0, 3), (1, 3), (1, 2), | |
(2, 2), (2, 1), (3, 1), (3, 0), (4, 0), (5, 0), (6, 0), (6, 1), (7, 1), (7, 2), (8, 2), (8, 3), (9, 3),(10, 3), | |
(10, 2), (11, 2), (11, 1), (12, 1), (12, 0), (13, 0), (14, 0), (15, 0), (15, 1), (15, 2), (15, 3), (15, 4), | |
(15, 5), (14, 5), (14, 6), (13, 6), (12, 6), (12, 5), (11, 5), (10, 5), (10, 6), (10, 7), (10, 8), (10, 9), | |
(10, 10), (9, 10), (8, 10), (8, 9), (7, 9), (7, 8), (7, 7), (7, 6), (7, 5), (7, 4), (6, 4), (6, 3), (5, 3), | |
(4, 3), (4, 4), (3, 4), (3, 5), (3, 6), (4, 6), (4, 7), (4, 8), (4, 9), (5, 9), (5, 10), (6, 10), (6, 11), | |
(6, 12), (7, 12), (7, 13), (8, 13), (9, 13), (10, 13), (11, 13), (12, 13), (12, 12), (13, 12), (13, 11), | |
(13, 10), (12, 10), (12, 9), (12, 8), (13, 8), (14, 8), (14, 9), (15, 9), (15, 10), (15, 11), (15, 12), | |
(15, 13), (14, 13), (14, 14), (13, 14), (13, 15), (12, 15), (11, 15), | |
], | |
"_tyamgin": [ | |
(1, 4), (1, 5), (2, 5), (3, 5), (4, 5), (4, 6), (4, 7), (3, 7), (2, 7), (1, 7), (0, 7), (0, 6), (0, 5), (1, 5), | |
(2, 5), (3, 5), (4, 5), (4, 4), (5, 4), (6, 4), (7, 4), (8, 4), (8, 5), (9, 5), (10, 5), (10, 6), (11, 6), | |
(12, 6), (13, 6), (13, 5), (14, 5), (15, 5), (15, 4), (15, 3), (15, 2), (14, 2), (13, 2), (12, 2), (12, 1), | |
(11, 1), (10, 1), (10, 2), (9, 2), (8, 2), (7, 2), (7, 3), (6, 3), (5, 3), (4, 3), (4, 2), (3, 2), (2, 2), | |
(1, 2), (0, 2), (0, 1), (0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (4, 1), (4, 2), (3, 2), (2, 2), (1, 2), (1, 3), | |
], | |
"map16": [ | |
(2, 12), (3, 12), (4, 12), (5, 12), (6, 12), (7, 12), (8, 12), (9, 12), | |
(9, 11), (10, 11), (10, 10), (11, 10), (11, 9), (12, 9), (12, 8), (13, 8), (13, 7), | |
(13, 6), (13, 5), (13, 4), (12, 4), (11, 4), (10, 4), | |
(9, 4), (8, 4), (8, 5), (7, 5), (7, 6), (6, 6), (6, 7), (5, 7), (5, 8), (5, 9), (5, 10), | |
(5, 11), (5, 12), (5, 13), (5, 14), (5, 15), | |
(4, 15), (3, 15), (2, 15), (2, 14), (2, 13), (2, 12), | |
(2, 11), (2, 10), (2, 9), (2, 8), (2, 7), | |
(2, 6), (3, 6), (3, 5), (4, 5), (4, 4), (5, 4), | |
(6, 4), (7, 4), (7, 5), (8, 5), (8, 6), (8, 7), (8, 8), (8, 9), (8, 10), (8, 11), (8, 12), (8, 13), (9, 13), (9, 14), (10, 14), (10, 15), | |
(11, 15), (12, 15), (13, 15), (13, 14), (13, 13), (13, 12), | |
(13, 11), (12, 11), (12, 10), (11, 10), (11, 9), (10, 9), (10, 8), (9, 8), (9, 7), | |
(8, 7), (7, 7), (6, 7), (5, 7), (4, 7), (3, 7), (2, 7), (1, 7), | |
(0, 7), (0, 8), (0, 9), (0, 10), (0, 11), (0, 12), (1, 12), | |
(2, 12), (3, 12), (4, 12), (5, 12), (6, 12), (7, 12), (8, 12), (9, 12), (10, 12), (11, 12), | |
(12, 12), (13, 12), (14, 12), (15, 12), | |
(15, 11), (15, 10), (15, 9), (15, 8), (15, 7), (15, 6), (15, 5), | |
(15, 4), (15, 3), (15, 2), (14, 2), (14, 1), (13, 1), | |
(13, 0), (12, 0), (11, 0), (11, 1), (10, 1), (9, 1), (8, 1), (7, 1), (6, 1), (5, 1), (5, 0), (4, 0), (3, 0), (2, 0), (2, 1), | |
(1, 1), (1, 2), (0, 2), (0, 3), (0, 4), (0, 5), | |
(0, 6), (0, 7), (0, 8), (0, 9), (0, 10), (0, 11), (0, 12), (1, 12), | |
], | |
"map17": [ | |
(2, 1), (3, 1), (4, 1), (4, 0), (5, 0), (6, 0), (7, 0), (8, 0), (9, 0), (10, 0), (11, 0), (12, 0), (13, 0), | |
(13, 1), (14, 1), (14, 2), (14, 3), (14, 4), (14, 5), | |
(14, 6), (13, 6), (13, 7), (12, 7), (12, 8), (11, 8), (11, 9), (10, 9), (10, 10), | |
(9, 10), (8, 10), (7, 10), (6, 10), (5, 10), (4, 10), (4, 9), | |
(4, 8), (5, 8), (6, 8), (7, 8), (7, 9), (8, 9), (8, 10), | |
(9, 10), (10, 10), (11, 10), (12, 10), (13, 10), | |
(13, 9), (14, 9), | |
(14, 8), (14, 7), (14, 6), (14, 5), (14, 4), (14, 3), (14, 2), (14, 1), | |
(13, 1), (13, 0), | |
(12, 0), (11, 0), (10, 0), (9, 0), (8, 0), (7, 0), (6, 0), (5, 0), (4, 0), | |
(4, 1), (3, 1), (3, 2), (2, 2), | |
(2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9), (2, 10), (2, 11), (2, 12), (2, 13), (2, 14), (2, 15), | |
(1, 15), (0, 15), | |
(0, 14), (0, 13), (0, 12), (0, 11), (0, 10), (0, 9), (0, 8), (0, 7), (0, 6), (0, 5), (0, 4), (0, 3), (0, 2), (0, 1), | |
(1, 1), | |
], | |
"map18": [ | |
(13, 12), (13, 11), (13, 10), (14, 10), (14, 9), (15, 9), (15, 8), (15, 7), (15, 6), (15, 5), (15, 4), (15, 3), | |
(14, 3), (13, 3), (12, 3), (11, 3), (10, 3), (9, 3), | |
(9, 2), (8, 2), (8, 1), (7, 1), (7, 0), | |
(6, 0), (5, 0), (4, 0), (3, 0), | |
(2, 0), (1, 0), (1, 1), | |
(0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8), (1, 8), (2, 8), | |
(2, 9), (2, 10), (2, 11), (1, 11), (0, 11), (0, 10), (0, 9), (0, 8), (1, 8), (2, 8), | |
(2, 9), (2, 10), (2, 11), (1, 11), (0, 11), | |
(0, 12), (0, 13), (1, 13), (1, 14), (2, 14), (2, 15), (3, 15), (4, 15), (5, 15), (6, 15), (7, 15), | |
(7, 14), (7, 13), (6, 13), (6, 12), (5, 12), (5, 11), (4, 11), (4, 10), (4, 9), (4, 8), (4, 7), (4, 6), (3, 6), | |
(2, 6), (2, 5), (2, 4), (2, 3), | |
(3, 3), (3, 2), (4, 2), (4, 3), (4, 4), (4, 5), (5, 5), (6, 5), (7, 5), (7, 6), | |
(7, 7), (7, 8), (7, 9), (7, 10), (8, 10), (9, 10), | |
(9, 9), (9, 8), (9, 7), (8, 7), (7, 7), (7, 8), (7, 9), (7, 10), (8, 10), (9, 10), | |
(9, 11), (9, 12), (9, 13), (9, 14), (9, 15), (10, 15), (11, 15), (12, 15), (13, 15), | |
(13, 14), (13, 13), | |
], | |
"map19": [ | |
(4, 5), (5, 5), (6, 5), (7, 5), | |
(7, 4), (7, 3), (7, 2), (7, 1), (7, 0), | |
(6, 0), (5, 0), (4, 0), (3, 0), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), | |
(1, 4), (0, 4), (0, 3), (0, 2), | |
(1, 2), (2, 2), (3, 2), (4, 2), (5, 2), | |
(5, 3), (5, 4), (5, 5), (5, 6), (5, 7), | |
(4, 7), (3, 7), (2, 7), | |
(2, 6), (2, 5), (3, 5), | |
], | |
"map20": [ | |
(2, 10), (2, 9), (2, 8), (2, 7), (2, 6), (2, 5), (2, 4), (3, 4), (4, 4), (5, 4), | |
(6, 4), (6, 5), (6, 6), (5, 6), (4, 6), (4, 5), (4, 4), (4, 3), (4, 2), | |
(5, 2), (6, 2), (7, 2), (8, 2), (9, 2), (10, 2), (11, 2), (12, 2), (13, 2), | |
(14, 2), (14, 1), (14, 0), (13, 0), (12, 0), (12, 1), (12, 2), (12, 3), (12, 4), | |
(12, 5), (12, 6), (12, 7), (12, 8), (12, 9), (12, 10), (11, 10), (10, 10), (9, 10), | |
(8, 10), (8, 9), (8, 8), (9, 8), (10, 8), (10, 9), (10, 10), (10, 11), | |
(10, 12), (9, 12), (8, 12), (7, 12), (6, 12), (5, 12), (4, 12), (3, 12), (2, 12), | |
(1, 12), (0, 12), (0, 13), (0, 14), (1, 14), (2, 14), (2, 13), (2, 12), (2, 11), | |
], | |
} | |
BacktrackTile = namedtuple("BacktrackTile", "i j weight") | |
class StopProjectileError(Exception): | |
pass | |
class MyStrategy: | |
def __init__(self): | |
# Track. | |
self.track = [] | |
self.track_index = 0 | |
# Stuck detection. | |
self.ticks_stuck = 0 | |
self.ticks_backwards = 0 | |
# Car type specific. | |
self.strategy = None | |
def move(self, me: Car, world: World, game: Game, move: Move): | |
if me.finished_track: | |
return | |
# Initialize track and car for the first time. | |
if not self.track: | |
print("World: %sx%s" % (world.width, world.height)) | |
print("Waypoints:", world.waypoints) | |
try: | |
self.track = PREDEFINED_TRACKS[world.map_name] | |
except KeyError: | |
print("Using init_track.") | |
self.init_track(me, game, world) | |
else: | |
print("Using predefined track.") | |
# self.check_track(self.track) | |
print("Track (%sx%s): %s" % (len(self.track), game.lap_count, self.track)) | |
if not self.strategy: | |
self.strategy = JeepCarStrategy(game) if me.type == CarType.JEEP else BuggyCarStrategy(game) | |
print("Using strategy: %s" % self.strategy) | |
# Frequently used values. | |
speed_squared = me.speed_x * me.speed_x + me.speed_y * me.speed_y | |
freeze_passed = (world.tick > game.initial_freeze_duration_ticks) | |
# Try to kill someone. | |
if freeze_passed: | |
self.kill_someone2(me, world, game, move) | |
# Update the following track point. | |
self.update_track_point(me, world, game) | |
current_i, current_j = self.get_me_i_j(me, game) | |
target_i, target_j = self.get_track_point(self.track_index) # next tile | |
# Default target point. | |
target_x, target_y = (target_i + 0.5) * game.track_tile_size, (target_j + 0.5) * game.track_tile_size | |
# Handling turns. | |
offset = 0.375 * game.track_tile_size # turn offset | |
dim1, djm1 = self.get_track_point_diff(self.track_index - 2) # 1 tile back | |
di1, dj1 = target_i - current_i, target_j - current_j # 1 tile further | |
di2, dj2 = self.get_track_point_diff(self.track_index) # 2 tiles further | |
di3, dj3 = self.get_track_point_diff(self.track_index + 1) # 3 tiles further | |
optimized_turn = ((di1, dj1) == (di3, dj3) and (dim1, djm1) == (di2, dj2)) | |
if (di1, dj1, di2, dj2) in ((0, -1, +1, 0), (-1, 0, 0, +1)): | |
# Up-right or left-down. | |
target_x += offset | |
target_y += offset | |
elif (di1, dj1, di2, dj2) in ((+1, 0, 0, +1), (0, -1, -1, 0)): | |
# Right-down or top-left. | |
target_x -= offset | |
target_y += offset | |
elif (di1, dj1, di2, dj2) in ((0, +1, -1, 0), (+1, 0, 0, -1)): | |
# Down-left or right-top. | |
target_x -= offset | |
target_y -= offset | |
elif (di1, dj1, di2, dj2) in ((-1, 0, 0, -1), (0, 1, 1, 0)): | |
# Left-up or down-right. | |
target_x += offset | |
target_y -= offset | |
else: | |
optimized_turn = False | |
target_x += di1 * 0.5 * game.track_tile_size | |
target_y += dj1 * 0.5 * game.track_tile_size | |
if optimized_turn: | |
target_x = (target_i + 0.5 + 0.5 * di2) * game.track_tile_size | |
target_y = (target_j + 0.5 + 0.5 * dj2) * game.track_tile_size | |
move.wheel_turn = me.get_angle_to(target_x, target_y) / (pi / 4.0) | |
move.engine_power = 1.0 | |
# Adjust speed. | |
if not optimized_turn and (di1, dj1) != (di2, dj2) and abs(speed_squared) > self.strategy.MAX_SPEED_TURN_VERY_SOON: | |
# There is a turn very soon… | |
move.brake = True | |
move.spill_oil = True | |
elif not optimized_turn and (di3, dj3) != (di2, dj2) and abs(speed_squared) > self.strategy.MAX_SPEED_TURN_SOON: | |
# There is a turn soon… | |
move.brake = True | |
elif optimized_turn or ( | |
(di1, dj1) == (di2, dj2) and | |
(di1, dj1) == self.get_track_point_diff(self.track_index + 2) and | |
(di1, dj1) == self.get_track_point_diff(self.track_index + 3) | |
): | |
# Use nitro! | |
move.use_nitro = freeze_passed and abs(move.wheel_turn) < self.strategy.MAX_WHEEL_TURN_USE_NITRO | |
# Any bonus? | |
min_bonus_distance = float('+inf') | |
for bonus in world.bonuses: | |
if optimized_turn: | |
break # Workaround. Too dangerous. | |
distance = me.get_distance_to_unit(bonus) | |
if ( | |
distance < 3.0 * game.track_tile_size and | |
abs(me.get_angle_to_unit(bonus) - me.get_angle_to(target_x, target_y)) < self.strategy.MAX_ANGLE_TO_BONUS and | |
distance < min_bonus_distance | |
): | |
move.wheel_turn = me.get_angle_to_unit(bonus) / (pi / 4.0) | |
min_bonus_distance = distance | |
# Stuck detection. | |
if self.ticks_backwards != 0: | |
# Moving backwards because of being stuck. | |
self.ticks_backwards -= 1 | |
move.engine_power = -0.3 | |
move.wheel_turn = -copysign(1.0, move.wheel_turn) | |
return # nothing to do anymore… | |
if freeze_passed and speed_squared < 1.0 and self.ticks_backwards == 0 and me.durability != 0.0: | |
self.ticks_stuck += 1 | |
if self.ticks_stuck > 50: | |
# Stuck. Set up backwards move. | |
print("Stuck!") | |
self.ticks_stuck = 0 | |
self.ticks_backwards = 200 | |
def update_track_point(self, me: Car, world: World, game: Game): | |
# Get current waypoint. | |
i, j = self.get_track_point(self.track_index) | |
if ( | |
(i * game.track_tile_size <= me.x <= (i + 1) * game.track_tile_size) and | |
(j * game.track_tile_size <= me.y <= (j + 1) * game.track_tile_size) | |
): | |
self.track_index += 1 | |
print("Tick #%s: move to #%s %s" % (world.tick, self.track_index, self.get_track_point(self.track_index))) | |
def get_track_point(self, i: int) -> Tuple[int, int]: | |
return self.track[i % len(self.track)] | |
def get_track_point_diff(self, i: int): | |
i1, j1 = self.get_track_point(i) | |
i2, j2 = self.get_track_point(i + 1) | |
return i2 - i1, j2 - j1 | |
def kill_someone2(self, me: Car, world: World, game: Game, move: Move): | |
if me.remaining_projectile_cooldown_ticks or not me.projectile_count: | |
return | |
threshold = me.width * me.width / 4.0 | |
for car in world.cars: | |
if car.player_id == me.player_id: | |
continue | |
target_x, target_y = car.x, car.y | |
projectile_x, projectile_y = me.x, me.y | |
projectile_speed_x = self.strategy.projectile_initial_speed * cos(me.angle) | |
projectile_speed_y = self.strategy.projectile_initial_speed * sin(me.angle) | |
# Simulate… | |
for tick in range(75): | |
target_x += car.speed_x | |
target_y += car.speed_y | |
try: | |
projectile_x, projectile_y, projectile_speed_x, projectile_speed_y = self.strategy.move_projectile( | |
world, game, projectile_x, projectile_y, projectile_speed_x, projectile_speed_y) | |
except StopProjectileError: | |
break | |
dx, dy = target_x - projectile_x, target_y - projectile_y | |
if dx * dx + dy * dy < threshold: | |
print("#%s Throw projectile (%s left): target: %s projectile: %s" % ( | |
world.tick, me.projectile_count, (target_x, target_y), (projectile_x, projectile_y))) | |
move.throw_projectile = True | |
return | |
def init_track(self, me: Car, game: Game, world: World): | |
turn_from_i, turn_from_j = None, None | |
last_i, last_j = self.get_me_i_j(me, game) | |
print("Me:", (last_i, last_j)) | |
waypoints = list(world.waypoints) | |
waypoints.append(waypoints.pop(0)) | |
for waypoint_i, waypoint_j in waypoints: | |
backtrack = {} | |
queue = deque([(last_i, last_j)]) | |
# Build a route from (last_i, last_j) to (i, j). | |
while queue: | |
current_i, current_j = queue.popleft() | |
# Direction left. | |
self.init_track_direction( | |
world, backtrack, queue, | |
turn_from_i, turn_from_j, current_i, current_j, current_i - 1, current_j, | |
TileType.CROSSROADS, TileType.HORIZONTAL, | |
TileType.RIGHT_TOP_CORNER, TileType.RIGHT_BOTTOM_CORNER, | |
TileType.LEFT_HEADED_T, TileType.TOP_HEADED_T, TileType.BOTTOM_HEADED_T, | |
) | |
# Direction right. | |
self.init_track_direction( | |
world, backtrack, queue, | |
turn_from_i, turn_from_j, current_i, current_j, current_i + 1, current_j, | |
TileType.CROSSROADS, TileType.HORIZONTAL, | |
TileType.LEFT_TOP_CORNER, TileType.LEFT_BOTTOM_CORNER, | |
TileType.RIGHT_HEADED_T, TileType.TOP_HEADED_T, TileType.BOTTOM_HEADED_T, | |
) | |
# Direction up. | |
self.init_track_direction( | |
world, backtrack, queue, | |
turn_from_i, turn_from_j, current_i, current_j, current_i, current_j - 1, | |
TileType.CROSSROADS, TileType.VERTICAL, | |
TileType.LEFT_BOTTOM_CORNER, TileType.RIGHT_BOTTOM_CORNER, | |
TileType.LEFT_HEADED_T, TileType.RIGHT_HEADED_T, TileType.TOP_HEADED_T, | |
) | |
# Direction down. | |
self.init_track_direction( | |
world, backtrack, queue, | |
turn_from_i, turn_from_j, current_i, current_j, current_i, current_j + 1, | |
TileType.CROSSROADS, TileType.VERTICAL, | |
TileType.LEFT_TOP_CORNER, TileType.RIGHT_TOP_CORNER, | |
TileType.LEFT_HEADED_T, TileType.RIGHT_HEADED_T, TileType.BOTTOM_HEADED_T, | |
) | |
# Backtrace the track part. | |
track_part = [] | |
i, j = waypoint_i, waypoint_j | |
print("Waypoint:", (waypoint_i, waypoint_j)) | |
while (i, j) != (last_i, last_j): | |
track_part.append((i, j)) | |
try: | |
print(backtrack[i, j], "->", (i, j)) | |
i, j, _ = backtrack[i, j] | |
except KeyError: | |
# Hotfix to pass "fog" pre-check. | |
break | |
# Make end of this part be a starting point of the next one. | |
last_i, last_j = waypoint_i, waypoint_j | |
print("Last:", (last_i, last_j)) | |
# Remember last turn to avoid abnormal turns at waypoints. | |
try: | |
turn_from_i, turn_from_j, _ = backtrack[waypoint_i, waypoint_j] | |
except KeyError: | |
# Hotfix to pass "fog" pre-check. | |
pass | |
# Extend track with the part. | |
self.track.extend(reversed(track_part)) | |
@classmethod | |
def init_track_direction( | |
cls, | |
world: World, backtrack: dict, queue: deque, | |
turn_from_i, turn_from_j, current_i, current_j, next_i, next_j, | |
*tile_types | |
): | |
if world.tiles_x_y[current_i][current_j] not in tile_types: | |
return | |
if (current_i, current_j) in backtrack: | |
# In the middle of a part of the track. | |
previous_i, previous_j, (previous_length, previous_turns) = backtrack[current_i, current_j] | |
length = previous_length + 1 | |
turns = previous_turns + cls.get_turn_count(previous_i, previous_j, current_i, current_j, next_i, next_j) | |
elif turn_from_i is not None and turn_from_j is not None: | |
# Next part of the track is just started but that's not the first part of the whole track. | |
length = 1 | |
turns = cls.get_turn_count(turn_from_i, turn_from_j, current_i, current_j, next_i, next_j) | |
if turns == 2: | |
# Prevent changing direction at the waypoint. | |
return | |
else: | |
# The very first part of the track. | |
length = 1 | |
turns = 0 | |
if (next_i, next_j) not in backtrack or backtrack[next_i, next_j].weight > (length, turns): | |
queue.append((next_i, next_j)) | |
backtrack[next_i, next_j] = BacktrackTile(current_i, current_j, (length, turns)) | |
@classmethod | |
def get_turn_count(cls, i1, j1, i2, j2, i3, j3): | |
di1, di2 = i2 - i1, i3 - i2 | |
dj1, dj2 = j2 - j1, j3 - j2 | |
cos_angle = di1 * di2 + dj1 * dj2 | |
if cos_angle == 1: | |
return 0 | |
if cos_angle == 0: | |
return 1 | |
if cos_angle == -1: | |
return 2 | |
raise ValueError("%s %s %s %s %s %s" % (i1, j1, i2, j2, i3, j3)) | |
@classmethod | |
def get_me_i_j(cls, me: Car, game: Game) -> (int, int): | |
return int(me.x // game.track_tile_size), int(me.y // game.track_tile_size) | |
@staticmethod | |
def check_track(track): | |
for i in range(1, len(track)): | |
if not ( | |
(track[i - 1][0] == track[i][0] and abs(track[i - 1][1] - track[i][1]) == 1) or | |
(abs(track[i - 1][0] - track[i][0]) == 1 and track[i - 1][1] == track[i][1]) | |
): | |
raise ValueError("invalid move: %s -> %s" % (track[i - 1], track[i])) | |
class AbstractCarStrategy: | |
MAX_SPEED_TURN_VERY_SOON = None | |
MAX_SPEED_TURN_SOON = None | |
MAX_WHEEL_TURN_USE_NITRO = None | |
MAX_ANGLE_TO_BONUS = None | |
def move_projectile(self, world: World, game: Game, projectile_x, projectile_y, projectile_speed_x, projectile_speed_y): | |
return projectile_x + projectile_speed_x, projectile_y + projectile_speed_y, projectile_speed_x, projectile_speed_y | |
class BuggyCarStrategy(AbstractCarStrategy): | |
MAX_SPEED_TURN_VERY_SOON = 250.0 | |
MAX_SPEED_TURN_SOON = 890.0 | |
MAX_WHEEL_TURN_USE_NITRO = 0.2 | |
MAX_ANGLE_TO_BONUS = pi / 12.0 | |
def __init__(self, game: Game): | |
self.projectile_initial_speed = game.washer_initial_speed | |
class JeepCarStrategy(AbstractCarStrategy): | |
MAX_SPEED_TURN_VERY_SOON = 200.0 | |
MAX_SPEED_TURN_SOON = 950.0 | |
MAX_WHEEL_TURN_USE_NITRO = 0.3 | |
MAX_ANGLE_TO_BONUS = pi / 11.0 | |
def __init__(self, game: Game): | |
self.projectile_initial_speed = game.tire_initial_speed | |
def move_projectile(self, world: World, game: Game, projectile_x, projectile_y, projectile_speed_x, projectile_speed_y): | |
new_projectile_x, new_projectile_y = projectile_x + projectile_speed_x, projectile_y + projectile_speed_y | |
i, j = self.get_i_j(game, projectile_x, projectile_y) | |
tile = world.tiles_x_y[i][j] | |
min_distance_squared = (game.tire_radius + game.track_tile_margin) ** 2 | |
# Top-left corner. | |
if tile in (TileType.RIGHT_BOTTOM_CORNER, TileType.LEFT_HEADED_T, TileType.TOP_HEADED_T, TileType.CROSSROADS): | |
dx = new_projectile_x - i * game.track_tile_size | |
dy = new_projectile_y - j * game.track_tile_size | |
if dx * dx + dy * dy < min_distance_squared: | |
raise StopProjectileError() | |
# Top-right corner. | |
if tile in (TileType.LEFT_BOTTOM_CORNER, TileType.RIGHT_HEADED_T, TileType.TOP_HEADED_T, TileType.CROSSROADS): | |
dx = new_projectile_x - (i + 1) * game.track_tile_size | |
dy = new_projectile_y - j * game.track_tile_size | |
if dx * dx + dy * dy < min_distance_squared: | |
raise StopProjectileError() | |
# Bottom-left corner. | |
if tile in (TileType.RIGHT_TOP_CORNER, TileType.BOTTOM_HEADED_T, TileType.LEFT_HEADED_T, TileType.CROSSROADS): | |
dx = new_projectile_x - i * game.track_tile_size | |
dy = new_projectile_y - (j + 1) * game.track_tile_size | |
if dx * dx + dy * dy < min_distance_squared: | |
raise StopProjectileError() | |
# Bottom-right corner. | |
if tile in (TileType.LEFT_TOP_CORNER, TileType.BOTTOM_HEADED_T, TileType.RIGHT_HEADED_T, TileType.CROSSROADS): | |
dx = new_projectile_x - (i + 1) * game.track_tile_size | |
dy = new_projectile_y - (j + 1) * game.track_tile_size | |
if dx * dx + dy * dy < min_distance_squared: | |
raise StopProjectileError() | |
# Up. | |
if tile in (TileType.HORIZONTAL, TileType.BOTTOM_HEADED_T, TileType.LEFT_TOP_CORNER, TileType.RIGHT_TOP_CORNER): | |
wall_y = j * game.track_tile_size + game.track_tile_margin + game.tire_radius | |
if new_projectile_y < wall_y < projectile_y: | |
raise StopProjectileError() # TODO: change angle. | |
return new_projectile_x, wall_y + (wall_y - new_projectile_y), projectile_speed_x, -projectile_speed_y | |
# Down. | |
if tile in (TileType.HORIZONTAL, TileType.TOP_HEADED_T, TileType.LEFT_BOTTOM_CORNER, TileType.RIGHT_BOTTOM_CORNER): | |
wall_y = (j + 1) * game.track_tile_size - game.track_tile_margin - game.tire_radius | |
if projectile_y < wall_y < new_projectile_y: | |
raise StopProjectileError() # TODO: change angle. | |
return new_projectile_x, wall_y + (wall_y - new_projectile_y), projectile_speed_x, -projectile_speed_y | |
# Left. | |
if tile in (TileType.VERTICAL, TileType.RIGHT_HEADED_T, TileType.LEFT_TOP_CORNER, TileType.LEFT_BOTTOM_CORNER): | |
wall_x = i * game.track_tile_size + game.track_tile_margin + game.tire_radius | |
if new_projectile_x < wall_x < projectile_x: | |
raise StopProjectileError() # TODO: change angle. | |
return wall_x + (wall_x - new_projectile_x), new_projectile_y, -projectile_speed_x, projectile_speed_y | |
# Right. | |
if tile in (TileType.VERTICAL, TileType.LEFT_HEADED_T, TileType.RIGHT_TOP_CORNER, TileType.RIGHT_BOTTOM_CORNER): | |
wall_x = (i + 1) * game.track_tile_size - game.track_tile_margin - game.tire_radius | |
if projectile_x < wall_x < new_projectile_x: | |
raise StopProjectileError() # TODO: change angle. | |
return wall_x + (wall_x - new_projectile_x), new_projectile_y, -projectile_speed_x, projectile_speed_y | |
return new_projectile_x, new_projectile_y, projectile_speed_x, projectile_speed_y | |
@staticmethod | |
def get_i_j(game: Game, x, y): | |
return int(x // game.track_tile_size), int(y // game.track_tile_size) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment