Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Racer tests #28

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
126 changes: 107 additions & 19 deletions src/enemy.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ struct Position {

trait PositionTrait {
// Returns the vertices of the enemy at given position
fn vertices(self: @Position) -> Span<Vec2>;
fn vertices(self: @Position) -> Span<Vec2>; // Position has unscaled members
fn vertices_scaled(self: @Position) -> Span<Vec2>; // Position has scaled members
}

impl PostionImpl of PositionTrait {
Expand Down Expand Up @@ -58,6 +59,34 @@ impl PostionImpl of PositionTrait {
vertices.append(Vec2 { x: FixedTrait::new_unscaled(*self.x + CAR_WIDTH, false), y: y1 });
vertices.span()
}

fn vertices_scaled(self: @Position) -> Span<Vec2> {
let mut vertices = ArrayTrait::new();
vertices
.append(
Vec2 {
x: FixedTrait::new(*self.x + CAR_WIDTH_SCALED, false),
y: FixedTrait::new(*self.y + CAR_HEIGHT_SCALED, false)
}
);

let x1 = if *self.x < CAR_WIDTH_SCALED {
FixedTrait::new(0, false)
} else {
FixedTrait::new(*self.x - CAR_WIDTH_SCALED, false)
};

let y1 = if *self.y < CAR_HEIGHT_SCALED {
FixedTrait::new(0, false)
} else {
FixedTrait::new(*self.y - CAR_HEIGHT_SCALED, false)
};

vertices.append(Vec2 { x: x1, y: FixedTrait::new(*self.y + CAR_HEIGHT_SCALED, false) });
vertices.append(Vec2 { x: x1, y: y1 });
vertices.append(Vec2 { x: FixedTrait::new(*self.x + CAR_WIDTH_SCALED, false), y: y1 });
vertices.span()
}
}

#[system]
Expand Down Expand Up @@ -271,7 +300,7 @@ mod move_enemies {
let new_y = if y <= velocity + height {
GRID_HEIGHT + y + height - velocity
} else {
y - (velocity + height)
y - velocity
};

Position {
Expand Down Expand Up @@ -438,7 +467,7 @@ mod tests {
use drive_ai::racer::{CAR_HEIGHT as CAR_HEIGHT_SCALED, CAR_WIDTH as CAR_WIDTH_SCALED};
use super::{Position, PositionTrait};

const HUNDRED: u128 = 1844674407370955161600;
const ONE_HUNDRED: u128 = 1844674407370955161600;

#[test]
#[available_gas(2000000)]
Expand All @@ -447,53 +476,112 @@ mod tests {
let vertices = position.vertices();

assert_precise(
*(vertices.at(0).x),
(HUNDRED + CAR_WIDTH_SCALED).into(),
*vertices.at(0).x,
(ONE_HUNDRED + CAR_WIDTH_SCALED).into(),
'invalid vertex_0',
Option::None(())
);
assert_precise(
*vertices.at(0).y,
(ONE_HUNDRED + CAR_HEIGHT_SCALED).into(),
'invalid vertex_0',
Option::None(())
);

assert_precise(
*vertices.at(1).x,
(ONE_HUNDRED - CAR_WIDTH_SCALED).into(),
'invalid vertex_1',
Option::None(())
);
assert_precise(
*vertices.at(1).y,
(ONE_HUNDRED + CAR_HEIGHT_SCALED).into(),
'invalid vertex_1',
Option::None(())
);

assert_precise(
*vertices.at(2).x,
(ONE_HUNDRED - CAR_WIDTH_SCALED).into(),
'invalid vertex_x2',
Option::None(())
);
assert_precise(
*vertices.at(2).y,
(ONE_HUNDRED - CAR_HEIGHT_SCALED).into(),
'invalid vertex_y2',
Option::None(())
);

assert_precise(
*vertices.at(3).x,
(ONE_HUNDRED + CAR_WIDTH_SCALED).into(),
'invalid vertex_3',
Option::None(())
);
assert_precise(
*vertices.at(3).y,
(ONE_HUNDRED - CAR_HEIGHT_SCALED).into(),
'invalid vertex_3',
Option::None(())
);
}

#[test]
#[available_gas(2000000)]
fn test_vertices_scaled() {
let position = Position { x: ONE_HUNDRED, y: ONE_HUNDRED };
let vertices = position.vertices_scaled();

assert_precise(
*vertices.at(0).x,
(ONE_HUNDRED + CAR_WIDTH_SCALED).into(),
'invalid vertex_0',
Option::None(())
);
assert_precise(
*(vertices.at(0).y),
(HUNDRED + CAR_HEIGHT_SCALED).into(),
*vertices.at(0).y,
(ONE_HUNDRED + CAR_HEIGHT_SCALED).into(),
'invalid vertex_0',
Option::None(())
);

assert_precise(
*(vertices.at(1).x),
(HUNDRED - CAR_WIDTH_SCALED).into(),
*vertices.at(1).x,
(ONE_HUNDRED - CAR_WIDTH_SCALED).into(),
'invalid vertex_1',
Option::None(())
);
assert_precise(
*(vertices.at(1).y),
(HUNDRED + CAR_HEIGHT_SCALED).into(),
*vertices.at(1).y,
(ONE_HUNDRED + CAR_HEIGHT_SCALED).into(),
'invalid vertex_1',
Option::None(())
);

assert_precise(
*(vertices.at(2).x),
(HUNDRED - CAR_WIDTH_SCALED).into(),
*vertices.at(2).x,
(ONE_HUNDRED - CAR_WIDTH_SCALED).into(),
'invalid vertex_x2',
Option::None(())
);
assert_precise(
*(vertices.at(2).y),
(HUNDRED - CAR_HEIGHT_SCALED).into(),
*vertices.at(2).y,
(ONE_HUNDRED - CAR_HEIGHT_SCALED).into(),
'invalid vertex_y2',
Option::None(())
);

assert_precise(
*(vertices.at(3).x),
(HUNDRED + CAR_WIDTH_SCALED).into(),
*vertices.at(3).x,
(ONE_HUNDRED + CAR_WIDTH_SCALED).into(),
'invalid vertex_3',
Option::None(())
);
assert_precise(
*(vertices.at(3).y),
(HUNDRED - CAR_HEIGHT_SCALED).into(),
*vertices.at(3).y,
(ONE_HUNDRED - CAR_HEIGHT_SCALED).into(),
'invalid vertex_3',
Option::None(())
);
Expand Down
80 changes: 52 additions & 28 deletions src/math.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ fn vertices(position: Vec2, width: Fixed, height: Fixed, theta: Fixed) -> Span<V
}

// Cool algorithm - see pp. 4-10 at https://www.dcs.gla.ac.uk/~pat/52233/slides/Geometry1x1.pdf
// TODO try to find cheaper algorithm
// Determines if segments p1q1 and p2q2 intersect
fn intersects(p1: Vec2, q1: Vec2, p2: Vec2, q2: Vec2) -> bool {
let orientation_a = orientation(p1, q1, p2);
Expand Down Expand Up @@ -89,14 +90,14 @@ fn orientation(a: Vec2, b: Vec2, c: Vec2) -> u8 {

// Finds distance from p1 to intersection of segments p1q1 and p2q2
fn distance(p1: Vec2, p2: Vec2, q2: Vec2, cos_ray: Fixed, sin_ray: Fixed) -> Fixed {
// All enemy edges are either vertical or horizontal
// All enemy edges are either vertical or horizontal; all walls are vertical
if p2.y == q2.y { // Enemy edge is horizontal
if p2.y == p1.y { // Ray is colinear with enemy edge
return min((p2.x - p1.x).abs(), (q2.x - p1.x).abs());
} else {
return ((p2.y - p1.y) / cos_ray).abs();
}
} else { // Enemy edge is vertical
} else { // Enemy edge or wall is vertical
if p2.x == p1.x { // Ray is colinear with enemy edge
return min((p2.y - p1.y).abs(), (q2.y - p1.y).abs());
} else {
Expand All @@ -105,6 +106,32 @@ fn distance(p1: Vec2, p2: Vec2, q2: Vec2, cos_ray: Fixed, sin_ray: Fixed) -> Fix
}
}

use debug::PrintTrait;
const DEFAULT_PRECISION: u128 = 1844674407370; // 1e-7
// Similar to cubit's assert_precise which works for Fixed type only, but here for u128
// To use `DEFAULT_PRECISION`, final arg is: `Option::None(())`.
// To use `custom_precision` of 184467440737_u128: `Option::Some(184467440737_u128)`.
fn assert_precise_u128(result: u128, expected: u128, msg: felt252, custom_precision: Option<u128>) {
let precision = match custom_precision {
Option::Some(val) => val,
Option::None(_) => DEFAULT_PRECISION,
};

if result < expected {
let diff = expected - result;
if (diff > precision) {
result.print();
assert(diff <= precision, msg);
}
} else {
let diff = result - expected;
if (diff > precision) {
result.print();
assert(diff <= precision, msg);
}
}
}

#[cfg(test)]
mod tests {
use traits::Into;
Expand All @@ -125,7 +152,7 @@ mod tests {
const FIFTY: u128 = 922337203685477580800;
const SIXTY: u128 = 1106804644422573096960;
const EIGHTY: u128 = 1475739525896764129280;
const HUNDRED: u128 = 1844674407370955161600;
const ONE_HUNDRED: u128 = 1844674407370955161600;
const DEG_30_IN_RADS: u128 = 9658715196994321226;
const DEG_90_IN_RADS: u128 = 28976077338029890953;

Expand Down Expand Up @@ -162,17 +189,17 @@ mod tests {

let mut vertices = vertices(position, width, height, theta);

assert_precise(*(vertices.at(0).x), TWENTY.into(), 'invalid vertex_0', Option::None(()));
assert_precise(*(vertices.at(0).y), FORTY.into(), 'invalid vertex_0', Option::None(()));
assert_precise(*vertices.at(0).x, TWENTY.into(), 'invalid vertex_0', Option::None(()));
assert_precise(*vertices.at(0).y, FORTY.into(), 'invalid vertex_0', Option::None(()));

assert_precise(*(vertices.at(1).x), 0, 'invalid vertex_1', Option::None(()));
assert_precise(*(vertices.at(1).y), FORTY.into(), 'invalid vertex_1', Option::None(()));
assert_precise(*vertices.at(1).x, 0, 'invalid vertex_1', Option::None(()));
assert_precise(*vertices.at(1).y, FORTY.into(), 'invalid vertex_1', Option::None(()));

assert_precise(*(vertices.at(2).x), 0, 'invalid vertex_2', Option::None(()));
assert_precise(*(vertices.at(2).y), 0, 'invalid vertex_2', Option::None(()));
assert_precise(*vertices.at(2).x, 0, 'invalid vertex_2', Option::None(()));
assert_precise(*vertices.at(2).y, 0, 'invalid vertex_2', Option::None(()));

assert_precise(*(vertices.at(3).x), TWENTY.into(), 'invalid vertex_3', Option::None(()));
assert_precise(*(vertices.at(3).y), 0, 'invalid vertex_3', Option::None(()));
assert_precise(*vertices.at(3).x, TWENTY.into(), 'invalid vertex_3', Option::None(()));
assert_precise(*vertices.at(3).y, 0, 'invalid vertex_3', Option::None(()));

let position = Vec2Trait::new(FixedTrait::new(TEN, false), FixedTrait::new(TWENTY, false));
let width = FixedTrait::new(TEN, false);
Expand All @@ -183,37 +210,34 @@ mod tests {

// x: ~8.66025403784439, y: ~42.32050807568880
assert_precise(
*(vertices.at(0).x), 159753090305067335160, 'invalid rotated vertex_0', Option::None(())
*vertices.at(0).x, 159753090305067335160, 'invalid rotated vertex_0', Option::None(())
);
assert_precise(
*(vertices.at(0).y), 780673828410437532220, 'invalid rotated vertex_0', Option::None(())
*vertices.at(0).y, 780673828410437532220, 'invalid rotated vertex_0', Option::None(())
);

// x: ~-8.66025403784439, y: ~32.32050807568880
assert_precise(
*(vertices.at(1).x),
-159752327071118592360,
'invalid rotated vertex_1',
Option::None(())
*vertices.at(1).x, -159752327071118592360, 'invalid rotated vertex_1', Option::None(())
);
assert_precise(
*(vertices.at(1).y), 596206769290316387460, 'invalid rotated vertex_1', Option::None(())
*vertices.at(1).y, 596206769290316387460, 'invalid rotated vertex_1', Option::None(())
);

// x: ~11.33974596215560, y: ~-2.32050807568877
assert_precise(
*(vertices.at(2).x), 209181791169123697160, 'invalid rotated vertex_2', Option::None(())
*vertices.at(2).x, 209181791169123697160, 'invalid rotated vertex_2', Option::None(())
);
assert_precise(
*(vertices.at(2).y), -42804065462055467580, 'invalid rotated vertex_2', Option::None(())
*vertices.at(2).y, -42804065462055467580, 'invalid rotated vertex_2', Option::None(())
);

// x: ~28.66025403784440, y: ~7.67949192431123
assert_precise(
*(vertices.at(3).x), 528687208545309624680, 'invalid rotated vertex_3', Option::None(())
*vertices.at(3).x, 528687208545309624680, 'invalid rotated vertex_3', Option::None(())
);
assert_precise(
*(vertices.at(3).y), 141662993658065677180, 'invalid rotated vertex_3', Option::None(())
*vertices.at(3).y, 141662993658065677180, 'invalid rotated vertex_3', Option::None(())
);
}

Expand All @@ -226,43 +250,43 @@ mod tests {
let mut p2 = Vec2Trait::new(FixedTrait::new(TEN, false), FixedTrait::new(FORTY, false));
let mut q2 = Vec2Trait::new(FixedTrait::new(THIRTY, false), FixedTrait::new(0, false));
let mut intersect = intersects(p1, q1, p2, q2);
assert(intersect == true, 'invalid intersection');
assert(intersect, 'invalid intersection');

// Switch only p1,q1
p1 = Vec2Trait::new(FixedTrait::new(0, false), FixedTrait::new(TEN, false));
q1 = Vec2Trait::new(FixedTrait::new(FORTY, false), FixedTrait::new(THIRTY, false));
p2 = Vec2Trait::new(FixedTrait::new(TEN, false), FixedTrait::new(FORTY, false));
q2 = Vec2Trait::new(FixedTrait::new(THIRTY, false), FixedTrait::new(0, false));
intersect = intersects(p1, q1, p2, q2);
assert(intersect == true, 'invalid intersection');
assert(intersect, 'invalid intersection');

// Switch only p2,q2
p1 = Vec2Trait::new(FixedTrait::new(FORTY, false), FixedTrait::new(THIRTY, false));
q1 = Vec2Trait::new(FixedTrait::new(0, false), FixedTrait::new(TEN, false));
p2 = Vec2Trait::new(FixedTrait::new(THIRTY, false), FixedTrait::new(0, false));
q2 = Vec2Trait::new(FixedTrait::new(TEN, false), FixedTrait::new(FORTY, false));
intersect = intersects(p1, q1, p2, q2);
assert(intersect == true, 'invalid intersection');
assert(intersect, 'invalid intersection');

// Switch both p1,q1 and p2,q2
p1 = Vec2Trait::new(FixedTrait::new(0, false), FixedTrait::new(TEN, false));
q1 = Vec2Trait::new(FixedTrait::new(FORTY, false), FixedTrait::new(THIRTY, false));
p2 = Vec2Trait::new(FixedTrait::new(THIRTY, false), FixedTrait::new(0, false));
q2 = Vec2Trait::new(FixedTrait::new(TEN, false), FixedTrait::new(FORTY, false));
intersect = intersects(p1, q1, p2, q2);
assert(intersect == true, 'invalid intersection');
assert(intersect, 'invalid intersection');

// Now shorter line 2 so no intersection
q2 = Vec2Trait::new(FixedTrait::new(TEN, false), FixedTrait::new(TEN, false));
intersect = intersects(p1, q1, p2, q2);
assert(intersect == false, 'invalid non-intersection');
assert(!intersect, 'invalid non-intersection');

// Colinear segments
q1 = Vec2Trait::new(FixedTrait::new(THIRTY, false), FixedTrait::new(TEN, false));
p2 = Vec2Trait::new(FixedTrait::new(TWENTY, false), FixedTrait::new(TEN, false));
q2 = Vec2Trait::new(FixedTrait::new(FORTY, false), FixedTrait::new(TEN, false));
intersect = intersects(p1, q1, p2, q2);
assert(intersect == true, 'invalid colinear intersection');
assert(intersect, 'invalid colinear intersection');
}

#[test]
Expand Down
Loading