Skip to content

Instantly share code, notes, and snippets.

@eigenein
Created April 26, 2016 18:33
Show Gist options
  • Save eigenein/8ea84e6c51f148355b6f9a50fc896068 to your computer and use it in GitHub Desktop.
Save eigenein/8ea84e6c51f148355b6f9a50fc896068 to your computer and use it in GitHub Desktop.
CodeRunner 2015
#!/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