Žádný typy, pouze inty
This commit is contained in:
parent
f8d03c6162
commit
6ffbfb0d1e
3 changed files with 101 additions and 119 deletions
|
@ -1,3 +1,4 @@
|
|||
"""The Asteracer game implementation. Includes the base movement code + couple of QOL additions (eg. save states)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
|
@ -5,14 +6,6 @@ import random
|
|||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from math import isqrt
|
||||
from typing import List, Union, Tuple, Dict
|
||||
|
||||
# tohle dříve byly numpy typy, ale asi je lepší,
|
||||
# ať účastníci nemusí nic instalovat...
|
||||
InstType = int # np.int8
|
||||
PosType = int # np.int64
|
||||
SpeedType = int # np.int64
|
||||
SizeType = int # np.int64
|
||||
|
||||
|
||||
class TickFlag:
|
||||
|
@ -23,18 +16,18 @@ class TickFlag:
|
|||
|
||||
@dataclass
|
||||
class Racer:
|
||||
x: PosType = 0
|
||||
y: PosType = 0
|
||||
vx: SpeedType = 0
|
||||
vy: SpeedType = 0
|
||||
radius: SizeType = 1
|
||||
x: int = 0
|
||||
y: int = 0
|
||||
vx: int = 0
|
||||
vy: int = 0
|
||||
radius: int = 1
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Asteroid:
|
||||
x: PosType = 0
|
||||
y: PosType = 0
|
||||
radius: SizeType = 1
|
||||
x: int = 0
|
||||
y: int = 0
|
||||
radius: int = 1
|
||||
|
||||
Goal = Asteroid
|
||||
|
||||
|
@ -42,7 +35,7 @@ Goal = Asteroid
|
|||
class Instruction:
|
||||
MAX_ACCELERATION = 127
|
||||
|
||||
def __init__(self, vx: Union[int, float] = 0, vy: Union[int, float] = 0):
|
||||
def __init__(self, vx: int | float = 0, vy: int | float = 0):
|
||||
"""Whatever values we get, normalize them."""
|
||||
|
||||
if distance_squared(vx, vy) > Instruction.MAX_ACCELERATION ** 2:
|
||||
|
@ -62,8 +55,8 @@ class Instruction:
|
|||
|
||||
assert distance_squared(vx, vy) <= Instruction.MAX_ACCELERATION ** 2
|
||||
|
||||
self.vx = InstType(vx)
|
||||
self.vy = InstType(vy)
|
||||
self.vx = vx
|
||||
self.vy = vy
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.vx, self.vy))
|
||||
|
@ -90,26 +83,26 @@ class Instruction:
|
|||
|
||||
@dataclass
|
||||
class BoundingBox:
|
||||
min_x: PosType
|
||||
min_y: PosType
|
||||
max_x: PosType
|
||||
max_y: PosType
|
||||
min_x: int
|
||||
min_y: int
|
||||
max_x: int
|
||||
max_y: int
|
||||
|
||||
def width(self) -> SizeType:
|
||||
return SizeType(self.max_x - self.min_x)
|
||||
def width(self) -> int:
|
||||
return int(self.max_x - self.min_x)
|
||||
|
||||
def height(self) -> SizeType:
|
||||
return SizeType(self.max_y - self.min_y)
|
||||
def height(self) -> int:
|
||||
return int(self.max_y - self.min_y)
|
||||
|
||||
|
||||
def distance_squared(x1, y1, x2=0, y2=0) -> PosType:
|
||||
def distance_squared(x1, y1, x2=0, y2=0) -> int:
|
||||
"""Squared Euclidean distance between two points."""
|
||||
return (PosType(x1) - PosType(x2)) ** 2 + (PosType(y1) - PosType(y2)) ** 2
|
||||
return (int(x1) - int(x2)) ** 2 + (int(y1) - int(y2)) ** 2
|
||||
|
||||
|
||||
def euclidean_distance(x1, y1, x2=0, y2=0):
|
||||
"""Integer Euclidean distance between two points. Uses integer square root."""
|
||||
return PosType(isqrt(distance_squared(x1, y1, x2, y2)))
|
||||
return int(isqrt(distance_squared(x1, y1, x2, y2)))
|
||||
|
||||
|
||||
def signum(x):
|
||||
|
@ -132,8 +125,8 @@ class Simulation:
|
|||
def __init__(
|
||||
self,
|
||||
racer: Racer = Racer(),
|
||||
asteroids: List[Asteroid] = None,
|
||||
goals: List[Goal] = None,
|
||||
asteroids: list[Asteroid] = None,
|
||||
goals: list[Goal] = None,
|
||||
bounding_box: BoundingBox = None,
|
||||
):
|
||||
# the initial racer state (used when resetting the simulation)
|
||||
|
@ -146,7 +139,7 @@ class Simulation:
|
|||
|
||||
# to speed up the computation, we divide the bounding box (if we have one) into a grid
|
||||
# we do this so we don't need to check all asteroids at each tick, only those that could collide with the racer
|
||||
self._grid: Dict[Tuple[int, int], List[Asteroid]] = defaultdict(list)
|
||||
self._grid: dict[tuple[int, int], list[Asteroid]] = defaultdict(list)
|
||||
|
||||
for asteroid in asteroids:
|
||||
min_x, min_y = self._coordinate_to_grid(
|
||||
|
@ -163,12 +156,12 @@ class Simulation:
|
|||
for grid_y in range(min_y, max_y + 1):
|
||||
self._grid[(grid_x, grid_y)].append(asteroid)
|
||||
|
||||
self.reached_goals: List[bool] = [False] * len(self.goals)
|
||||
self.reached_goals: list[bool] = [False] * len(self.goals)
|
||||
|
||||
# a list of simulation states that can be popped (restored to)
|
||||
self._pushed_states = []
|
||||
|
||||
def _coordinate_to_grid(self, x: float, y: float) -> Tuple[int, int]:
|
||||
def _coordinate_to_grid(self, x: float, y: float) -> tuple[int, int]:
|
||||
"""Translate an (x,y) coordinate into a coordinate of the grid."""
|
||||
return (x // self.CELL_SIZE, y // self.CELL_SIZE)
|
||||
|
||||
|
@ -181,14 +174,14 @@ class Simulation:
|
|||
self.racer.vy = division(self.racer.vy * self.DRAG_FRACTION[0], self.DRAG_FRACTION[1])
|
||||
|
||||
# velocity
|
||||
self.racer.vx += SpeedType(vx)
|
||||
self.racer.vy += SpeedType(vy)
|
||||
self.racer.vx += int(vx)
|
||||
self.racer.vy += int(vy)
|
||||
|
||||
# movement
|
||||
self.racer.x += self.racer.vx
|
||||
self.racer.y += self.racer.vy
|
||||
|
||||
def _push_out(self, obj: Union[Asteroid, BoundingBox]) -> bool:
|
||||
def _push_out(self, obj: Asteroid | BoundingBox) -> bool:
|
||||
"""Attempt to push the racer out of the object (if he's colliding), adjusting
|
||||
his velocity accordingly (based on the angle of collision). Returns True if the
|
||||
racer was pushed out, otherwise returns False."""
|
||||
|
@ -292,7 +285,7 @@ class Simulation:
|
|||
|
||||
return (TickFlag.COLLIDED if collided else 0) | (TickFlag.GOAL_REACHED if goal else 0)
|
||||
|
||||
def simulate(self, instructions: List[Instruction]):
|
||||
def simulate(self, instructions: list[Instruction]):
|
||||
"""Simulate a number of instructions for the simulation (from the start)."""
|
||||
self.restart()
|
||||
|
||||
|
@ -337,10 +330,10 @@ class Simulation:
|
|||
lines = f.read().splitlines()
|
||||
|
||||
racer_parts = lines[0].split()
|
||||
racer = Racer(x=PosType(racer_parts[0]), y=PosType(racer_parts[1]), radius=SizeType(racer_parts[2]))
|
||||
racer = Racer(x=int(racer_parts[0]), y=int(racer_parts[1]), radius=int(racer_parts[2]))
|
||||
|
||||
bb_parts = lines[1].split()
|
||||
bb = BoundingBox(PosType(bb_parts[0]), PosType(bb_parts[1]), PosType(bb_parts[2]), PosType(bb_parts[2]))
|
||||
bb = BoundingBox(int(bb_parts[0]), int(bb_parts[1]), int(bb_parts[2]), int(bb_parts[2]))
|
||||
|
||||
asteroid_count = int(lines[2])
|
||||
|
||||
|
@ -349,9 +342,9 @@ class Simulation:
|
|||
asteroid_parts = lines[i].split()
|
||||
asteroids.append(
|
||||
Asteroid(
|
||||
x=PosType(asteroid_parts[0]),
|
||||
y=PosType(asteroid_parts[1]),
|
||||
radius=SizeType(asteroid_parts[2]),
|
||||
x=int(asteroid_parts[0]),
|
||||
y=int(asteroid_parts[1]),
|
||||
radius=int(asteroid_parts[2]),
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -362,9 +355,9 @@ class Simulation:
|
|||
goal_parts = lines[i].split()
|
||||
goals.append(
|
||||
Asteroid(
|
||||
x=PosType(goal_parts[0]),
|
||||
y=PosType(goal_parts[1]),
|
||||
radius=SizeType(goal_parts[2]),
|
||||
x=int(goal_parts[0]),
|
||||
y=int(goal_parts[1]),
|
||||
radius=int(goal_parts[2]),
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -390,7 +383,7 @@ class Simulation:
|
|||
self.reached_goals = list(self._pushed_states[-1][1])
|
||||
|
||||
|
||||
def save_instructions(path: str, instructions: List[Instruction]):
|
||||
def save_instructions(path: str, instructions: list[Instruction]):
|
||||
"""Save a list of instructions to a file:
|
||||
| 4 // number if instructions
|
||||
| -16 -127 // instructions...
|
||||
|
@ -405,13 +398,13 @@ def save_instructions(path: str, instructions: List[Instruction]):
|
|||
f.write(f"{instruction.vx} {instruction.vy}\n")
|
||||
|
||||
|
||||
def load_instructions(path: str) -> List[Instruction]:
|
||||
def load_instructions(path: str) -> list[Instruction]:
|
||||
"""Load a list of instructions from a file (see save_instructions for the format description)."""
|
||||
instructions = []
|
||||
|
||||
with open(path) as f:
|
||||
for line in f.read().splitlines()[1:]:
|
||||
instruction_parts = list(map(InstType, line.split()))
|
||||
instruction_parts = list(map(int, line.split()))
|
||||
instructions.append(Instruction(*instruction_parts))
|
||||
|
||||
return instructions
|
||||
|
|
|
@ -12,61 +12,50 @@ pub mod TickFlag {
|
|||
|
||||
pub type TickResult = usize;
|
||||
|
||||
pub type InstType = i8;
|
||||
pub type PosType = i64;
|
||||
pub type SpeedType = i64;
|
||||
pub type SizeType = i64;
|
||||
pub static MAX_ACCELERATION: isize = 127;
|
||||
|
||||
pub static MAX_ACCELERATION: InstType = 127;
|
||||
|
||||
pub static DRAG_FRACTION: (SpeedType, SpeedType) = (9, 10);
|
||||
pub static COLLISION_FRACTION: (SpeedType, SpeedType) = (1, 2);
|
||||
pub static DRAG_FRACTION: (isize, isize) = (9, 10);
|
||||
pub static COLLISION_FRACTION: (isize, isize) = (1, 2);
|
||||
pub static MAX_COLLISION_RESOLUTIONS: usize = 5;
|
||||
|
||||
pub static CELL_SIZE: PosType = 10_000;
|
||||
pub static CELL_SIZE: isize = 10_000;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Racer {
|
||||
pub x: PosType,
|
||||
pub y: PosType,
|
||||
pub vx: SpeedType,
|
||||
pub vy: SpeedType,
|
||||
pub radius: SizeType,
|
||||
pub x: isize,
|
||||
pub y: isize,
|
||||
pub vx: isize,
|
||||
pub vy: isize,
|
||||
pub radius: isize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Asteroid {
|
||||
pub x: PosType,
|
||||
pub y: PosType,
|
||||
pub radius: SizeType,
|
||||
pub x: isize,
|
||||
pub y: isize,
|
||||
pub radius: isize,
|
||||
}
|
||||
|
||||
pub type Goal = Asteroid;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct Instruction {
|
||||
pub vx: InstType,
|
||||
pub vy: InstType,
|
||||
pub vx: isize,
|
||||
pub vy: isize,
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
fn valid(vx: PosType, vy: PosType) -> bool {
|
||||
distance_squared(vx, vy, 0, 0) <= (MAX_ACCELERATION as PosType).pow(2)
|
||||
fn valid(vx: isize, vy: isize) -> bool {
|
||||
distance_squared(vx, vy, 0, 0) <= (MAX_ACCELERATION).pow(2)
|
||||
}
|
||||
|
||||
pub fn new<T>(vx: T, vy: T) -> Self
|
||||
where
|
||||
T: Copy + Into<PosType>,
|
||||
{
|
||||
let vx: i64 = vx.into();
|
||||
let vy: i64 = vy.into();
|
||||
|
||||
pub fn new(vx: isize, vy: isize) -> Self {
|
||||
if !Self::valid(vx, vy) {
|
||||
// use float to properly normalize here
|
||||
let float_distance = ((vx as f64).powf(2.) + (vy as f64).powf(2.)).powf(1. / 2.);
|
||||
|
||||
let mut vx = ((vx as f64 / float_distance) * MAX_ACCELERATION as f64) as PosType;
|
||||
let mut vy = ((vy as f64 / float_distance) * MAX_ACCELERATION as f64) as PosType;
|
||||
let mut vx = ((vx as f64 / float_distance) * MAX_ACCELERATION as f64) as isize;
|
||||
let mut vy = ((vy as f64 / float_distance) * MAX_ACCELERATION as f64) as isize;
|
||||
|
||||
// if we're still over, decrement both values
|
||||
if !Self::valid(vx, vy) {
|
||||
|
@ -74,14 +63,14 @@ impl Instruction {
|
|||
vy -= vy.signum();
|
||||
}
|
||||
|
||||
return Self { vx: vx as InstType, vy: vy as InstType };
|
||||
return Self { vx, vy };
|
||||
}
|
||||
|
||||
assert!(Self::valid(vx, vy));
|
||||
|
||||
Self {
|
||||
vx: vx as InstType,
|
||||
vy: vy as InstType,
|
||||
vx,
|
||||
vy,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,8 +78,8 @@ impl Instruction {
|
|||
let mut rng = rand::rng();
|
||||
|
||||
Self {
|
||||
vx: rng.random::<InstType>(),
|
||||
vy: rng.random::<InstType>(),
|
||||
vx: rng.random::<i64>() as isize,
|
||||
vy: rng.random::<i64>() as isize,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,8 +99,8 @@ impl Instruction {
|
|||
.collect::<Vec<&str>>();
|
||||
|
||||
instructions.push(Instruction {
|
||||
vx: parts[0].parse::<InstType>().unwrap(),
|
||||
vy: parts[1].parse::<InstType>().unwrap(),
|
||||
vx: parts[0].parse::<isize>().unwrap(),
|
||||
vy: parts[1].parse::<isize>().unwrap(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -130,24 +119,24 @@ impl Instruction {
|
|||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BoundingBox {
|
||||
pub min_x: SizeType,
|
||||
pub min_y: SizeType,
|
||||
pub max_x: SizeType,
|
||||
pub max_y: SizeType,
|
||||
pub min_x: isize,
|
||||
pub min_y: isize,
|
||||
pub max_x: isize,
|
||||
pub max_y: isize,
|
||||
}
|
||||
|
||||
impl BoundingBox {
|
||||
pub fn width(&self) -> SizeType {
|
||||
pub fn width(&self) -> isize {
|
||||
self.max_x - self.min_x
|
||||
}
|
||||
|
||||
pub fn height(&self) -> SizeType {
|
||||
pub fn height(&self) -> isize {
|
||||
self.max_y - self.min_y
|
||||
}
|
||||
}
|
||||
|
||||
/// Squared Euclidean distance; useful for distance checks.
|
||||
fn distance_squared(x1: PosType, y1: PosType, x2: PosType, y2: PosType) -> PosType {
|
||||
fn distance_squared(x1: isize, y1: isize, x2: isize, y2: isize) -> isize {
|
||||
(x1 - x2).pow(2) + (y1 - y2).pow(2)
|
||||
}
|
||||
|
||||
|
@ -155,8 +144,8 @@ fn distance_squared(x1: PosType, y1: PosType, x2: PosType, y2: PosType) -> PosTy
|
|||
///
|
||||
/// Note: this implementation might break for larger position values, but since
|
||||
/// the maps are never going to be this large, I'm not fixing it now.
|
||||
pub fn euclidean_distance(x1: PosType, y1: PosType, x2: PosType, y2: PosType) -> PosType {
|
||||
(distance_squared(x1, y1, x2, y2) as f64).sqrt() as PosType
|
||||
pub fn euclidean_distance(x1: isize, y1: isize, x2: isize, y2: isize) -> isize {
|
||||
(distance_squared(x1, y1, x2, y2) as f64).sqrt() as isize
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -170,8 +159,8 @@ pub struct Simulation {
|
|||
|
||||
pub reached_goals: Vec<bool>,
|
||||
|
||||
_grid: HashMap<(PosType, PosType), Vec<Asteroid>>,
|
||||
_cell_size: PosType,
|
||||
_grid: HashMap<(isize, isize), Vec<Asteroid>>,
|
||||
_cell_size: isize,
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -234,7 +223,7 @@ impl Simulation {
|
|||
simulation
|
||||
}
|
||||
|
||||
fn coordinate_to_grid(&self, x: PosType, y: PosType) -> (PosType, PosType) {
|
||||
fn coordinate_to_grid(&self, x: isize, y: isize) -> (isize, isize) {
|
||||
(x / self._cell_size, y / self._cell_size)
|
||||
}
|
||||
|
||||
|
@ -242,11 +231,11 @@ impl Simulation {
|
|||
self.racer.vx = (self.racer.vx * DRAG_FRACTION.0) / DRAG_FRACTION.1;
|
||||
self.racer.vy = (self.racer.vy * DRAG_FRACTION.0) / DRAG_FRACTION.1;
|
||||
|
||||
self.racer.vx += instruction.vx as SpeedType;
|
||||
self.racer.vy += instruction.vy as SpeedType;
|
||||
self.racer.vx += instruction.vx;
|
||||
self.racer.vy += instruction.vy;
|
||||
|
||||
self.racer.x += self.racer.vx as PosType;
|
||||
self.racer.y += self.racer.vy as PosType;
|
||||
self.racer.x += self.racer.vx as isize;
|
||||
self.racer.y += self.racer.vy as isize;
|
||||
}
|
||||
|
||||
fn push_from_asteroids(&mut self) -> bool {
|
||||
|
@ -413,9 +402,9 @@ impl Simulation {
|
|||
let racer_parts = parts_fn();
|
||||
|
||||
let racer = Racer {
|
||||
x: racer_parts[0].parse::<PosType>().unwrap(),
|
||||
y: racer_parts[1].parse::<PosType>().unwrap(),
|
||||
radius: racer_parts[2].parse::<SizeType>().unwrap(),
|
||||
x: racer_parts[0].parse::<isize>().unwrap(),
|
||||
y: racer_parts[1].parse::<isize>().unwrap(),
|
||||
radius: racer_parts[2].parse::<isize>().unwrap(),
|
||||
vx: 0,
|
||||
vy: 0,
|
||||
};
|
||||
|
@ -423,10 +412,10 @@ impl Simulation {
|
|||
let bb_parts = parts_fn();
|
||||
|
||||
let bbox = BoundingBox {
|
||||
min_x: bb_parts[0].parse::<SizeType>().unwrap(),
|
||||
min_y: bb_parts[1].parse::<SizeType>().unwrap(),
|
||||
max_x: bb_parts[2].parse::<SizeType>().unwrap(),
|
||||
max_y: bb_parts[3].parse::<SizeType>().unwrap(),
|
||||
min_x: bb_parts[0].parse::<isize>().unwrap(),
|
||||
min_y: bb_parts[1].parse::<isize>().unwrap(),
|
||||
max_x: bb_parts[2].parse::<isize>().unwrap(),
|
||||
max_y: bb_parts[3].parse::<isize>().unwrap(),
|
||||
};
|
||||
|
||||
let asteroid_count = parts_fn()[0].parse::<usize>().unwrap();
|
||||
|
@ -436,9 +425,9 @@ impl Simulation {
|
|||
let asteroid_parts = parts_fn();
|
||||
|
||||
asteroids.push(Asteroid {
|
||||
x: asteroid_parts[0].parse::<PosType>().unwrap(),
|
||||
y: asteroid_parts[1].parse::<PosType>().unwrap(),
|
||||
radius: asteroid_parts[2].parse::<PosType>().unwrap(),
|
||||
x: asteroid_parts[0].parse::<isize>().unwrap(),
|
||||
y: asteroid_parts[1].parse::<isize>().unwrap(),
|
||||
radius: asteroid_parts[2].parse::<isize>().unwrap(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -449,9 +438,9 @@ impl Simulation {
|
|||
let goal_parts = parts_fn();
|
||||
|
||||
goals.push(Asteroid {
|
||||
x: goal_parts[0].parse::<PosType>().unwrap(),
|
||||
y: goal_parts[1].parse::<PosType>().unwrap(),
|
||||
radius: goal_parts[2].parse::<PosType>().unwrap(),
|
||||
x: goal_parts[0].parse::<isize>().unwrap(),
|
||||
y: goal_parts[1].parse::<isize>().unwrap(),
|
||||
radius: goal_parts[2].parse::<isize>().unwrap(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::path::PathBuf;
|
|||
mod asteracer;
|
||||
|
||||
pub fn load_asteroid_graph(path: &PathBuf) -> (
|
||||
Vec<(PosType, PosType)>,
|
||||
Vec<(isize, isize)>,
|
||||
Vec<(usize, usize)>,
|
||||
Vec<(char, usize)>,
|
||||
) {
|
||||
|
@ -37,7 +37,7 @@ pub fn load_asteroid_graph(path: &PathBuf) -> (
|
|||
|
||||
// Load vertices
|
||||
for i in 0..(n_racer + n_asteroid + n_goal) {
|
||||
let line: Vec<i64> = iter
|
||||
let line: Vec<isize> = iter
|
||||
.next()
|
||||
.unwrap()
|
||||
.split_whitespace()
|
||||
|
@ -120,7 +120,7 @@ fn main() {
|
|||
// posbíráme zbývající cíle tak, že k nim poletíme přímou čarou
|
||||
while simulation.reached_goals.iter().any(|&reached| !reached) {
|
||||
let mut nearest_goal = None;
|
||||
let mut nearest_goal_distance = PosType::MAX;
|
||||
let mut nearest_goal_distance = isize::MAX;
|
||||
|
||||
for (i, &reached) in simulation.reached_goals.iter().enumerate() {
|
||||
if !reached {
|
||||
|
|
Loading…
Reference in a new issue