.
.
This commit is contained in:
@@ -0,0 +1,123 @@
|
|||||||
|
use alloc::borrow::ToOwned;
|
||||||
|
use alloc::string::String;
|
||||||
|
use alloc::vec;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use crate::std::application::{Application, Error};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use crate::kernel::render::{Color, ColorCode, ScreenChar};
|
||||||
|
use crate::{println, serial_println};
|
||||||
|
use crate::std::frame::ColouredElement;
|
||||||
|
use crate::std::io::{Screen, Stdin};
|
||||||
|
use crate::std::time::wait;
|
||||||
|
use crate::user::bin::snake::Game;
|
||||||
|
|
||||||
|
pub struct GameOfLife {
|
||||||
|
frame: [[ScreenChar; 80]; 25],
|
||||||
|
}
|
||||||
|
|
||||||
|
const LOOP_SPEED: f64 = 0.1;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Application for GameOfLife {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
frame: [[ScreenChar::null(); 80]; 25],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn run(&mut self, args: Vec<String>) -> Result<(), Error> {
|
||||||
|
// setup:
|
||||||
|
Screen::application_mode();
|
||||||
|
|
||||||
|
let xoffset = 38;
|
||||||
|
let yoffset = 5;
|
||||||
|
|
||||||
|
// example pattern
|
||||||
|
self.activate(0 + xoffset, 1 + yoffset);
|
||||||
|
self.activate(1 + xoffset, 0 + yoffset);
|
||||||
|
self.activate(1 + xoffset, 1 + yoffset);
|
||||||
|
self.activate(2 + xoffset, 1 + yoffset);
|
||||||
|
|
||||||
|
self.activate(0 + xoffset, 4 + yoffset);
|
||||||
|
self.activate(1 + xoffset, 4 + yoffset);
|
||||||
|
self.activate(2 + xoffset, 4 + yoffset);
|
||||||
|
|
||||||
|
self.activate(0 + xoffset, 6 + yoffset);
|
||||||
|
self.activate(2 + xoffset, 6 + yoffset);
|
||||||
|
self.activate(0 + xoffset, 7 + yoffset);
|
||||||
|
self.activate(2 + xoffset, 7 + yoffset);
|
||||||
|
|
||||||
|
self.activate(0 + xoffset, 9 + yoffset);
|
||||||
|
self.activate(1 + xoffset, 9 + yoffset);
|
||||||
|
self.activate(2 + xoffset, 9 + yoffset);
|
||||||
|
|
||||||
|
self.activate(0 + xoffset, 12 + yoffset);
|
||||||
|
self.activate(1 + xoffset, 13 + yoffset);
|
||||||
|
self.activate(1 + xoffset, 12 + yoffset);
|
||||||
|
self.activate(2 + xoffset, 12 + yoffset);
|
||||||
|
|
||||||
|
self.mainloop()?;
|
||||||
|
|
||||||
|
Screen::terminal_mode();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameOfLife {
|
||||||
|
fn activate(&mut self, x: u8, y: u8) {
|
||||||
|
self.frame[24 - y as usize][x as usize] = ScreenChar::new('#' as u8, ColorCode::new(Color::Green, Color::Black));
|
||||||
|
}
|
||||||
|
fn mainloop(&mut self) -> Result<(), Error> {
|
||||||
|
'mainloop: loop {
|
||||||
|
// render element previous frame before resetting.
|
||||||
|
|
||||||
|
wait(LOOP_SPEED);
|
||||||
|
|
||||||
|
self.render().map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?;
|
||||||
|
match Stdin::try_keystroke() {
|
||||||
|
Some('x') => break 'mainloop,
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Logic goes here
|
||||||
|
|
||||||
|
let mut new_frame = [[ScreenChar::null(); 80]; 25];
|
||||||
|
|
||||||
|
self.frame.iter().enumerate().for_each(|(y, row)| row.iter().enumerate().for_each(|(x, chr)| {
|
||||||
|
new_frame[y][x] = self.get_new_value(x as u8, y as u8);
|
||||||
|
}));
|
||||||
|
|
||||||
|
self.frame = new_frame;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_new_value(&self, x: u8, y: u8) -> ScreenChar {
|
||||||
|
let adjacent = vec![(0i32, 1i32), (0, -1), (1, 0), (-1, 0), (1, 1), (1, -1), (-1, 1), (-1, -1)].into_iter().map(|(relx, rely)| {
|
||||||
|
(x as i32 + relx, y as i32 + rely)
|
||||||
|
}).filter(|(absx, absy)| {
|
||||||
|
0 <= *absx && *absx < 80 && 0 <= *absy && *absy < 25
|
||||||
|
}).collect::<Vec<(i32, i32)>>();
|
||||||
|
|
||||||
|
let alive = adjacent.iter().filter(|(x, y)| self.frame[*y as usize][*x as usize] == ScreenChar::new('#' as u8, ColorCode::new(Color::Green, Color::Black))).count();
|
||||||
|
|
||||||
|
if alive == 2 {
|
||||||
|
if self.frame[y as usize][x as usize] == ScreenChar::new('#' as u8, ColorCode::new(Color::Green, Color::Black)) {
|
||||||
|
return ScreenChar::new('#' as u8, ColorCode::new(Color::Green, Color::Black));
|
||||||
|
} else {
|
||||||
|
return ScreenChar::null();
|
||||||
|
}
|
||||||
|
} else if alive == 3 {
|
||||||
|
ScreenChar::new('#' as u8, ColorCode::new(Color::Green, Color::Black))
|
||||||
|
} else {
|
||||||
|
ScreenChar::null()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self) -> Result<(), ()> {
|
||||||
|
let cloned_frame = self.frame.iter().map(|r| r.iter().map(|c| c.clone()).collect::<Vec<ScreenChar>>()).collect::<Vec<Vec<ScreenChar>>>();
|
||||||
|
let mut elem = ColouredElement::generate(cloned_frame, (80, 25));
|
||||||
|
elem.render((0,0));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,3 +9,5 @@ mod gigachad_detector;
|
|||||||
//mod shellrewrite;
|
//mod shellrewrite;
|
||||||
mod snake;
|
mod snake;
|
||||||
mod grapher;
|
mod grapher;
|
||||||
|
mod gameoflife;
|
||||||
|
mod tetris;
|
||||||
|
|||||||
+31
-21
@@ -6,7 +6,7 @@ use x86_64::instructions::interrupts;
|
|||||||
use alloc::{boxed::Box, string::{String, ToString}, vec, vec::Vec};
|
use alloc::{boxed::Box, string::{String, ToString}, vec, vec::Vec};
|
||||||
use vga::writers::{GraphicsWriter, PrimitiveDrawing};
|
use vga::writers::{GraphicsWriter, PrimitiveDrawing};
|
||||||
|
|
||||||
use crate::{print, println, std, std::application::{Application, Error}, user::bin::*};
|
use crate::{print, printerr, println, std, std::application::{Application, Error}, user::bin::*};
|
||||||
use crate::std::io::{Color, write, Screen, Stdin};
|
use crate::std::io::{Color, write, Screen, Stdin};
|
||||||
use crate::std::random::Random;
|
use crate::std::random::Random;
|
||||||
use crate::user::bin::gigachad_detector::GigachadDetector;
|
use crate::user::bin::gigachad_detector::GigachadDetector;
|
||||||
@@ -51,11 +51,19 @@ pub async fn eventloop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_error(e: Error) {
|
fn handle_error(e: Error) {
|
||||||
if let Error::ApplicationError(s) = e {
|
match e {
|
||||||
println!("there was an error! exiting program!: {}", s);
|
Error::EmptyCommand => {
|
||||||
} else {
|
printerr!("empty command");
|
||||||
println!("there was an error! exiting program!");
|
},
|
||||||
|
Error::UnknownCommand(cmd_str) => {
|
||||||
|
printerr!("unknown command: '{}'", cmd_str);
|
||||||
|
},
|
||||||
|
Error::ApplicationError(e) => {
|
||||||
|
printerr!("application returned error:\n{}", e);
|
||||||
|
},
|
||||||
|
Error::CommandFailed(e) => {
|
||||||
|
printerr!("command failed:\n{}", e);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,8 +120,17 @@ async fn exec() -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
"snake" => {
|
"snake" => {
|
||||||
let mut game = snake::Game::new();
|
let mut game = snake::Game::new();
|
||||||
game.run(Vec::new()).await;
|
game.run(args).await?;
|
||||||
}
|
}
|
||||||
|
"gameoflife" => {
|
||||||
|
let mut game = gameoflife::GameOfLife::new();
|
||||||
|
game.run(Vec::new()).await?;
|
||||||
|
}
|
||||||
|
"tetris" => {
|
||||||
|
let mut game = tetris::TetrisEngine::new();
|
||||||
|
game.run(Vec::new()).await?;
|
||||||
|
}
|
||||||
|
|
||||||
"gigachad?" => {
|
"gigachad?" => {
|
||||||
let mut gigachad_detector = GigachadDetector::new();
|
let mut gigachad_detector = GigachadDetector::new();
|
||||||
gigachad_detector.run(args).await?;
|
gigachad_detector.run(args).await?;
|
||||||
@@ -122,13 +139,12 @@ async fn exec() -> Result<(), Error> {
|
|||||||
"wait" => {
|
"wait" => {
|
||||||
use std::time::wait;
|
use std::time::wait;
|
||||||
|
|
||||||
for _ in 0..20 {
|
if args.len() != 1 {
|
||||||
wait(0.5);
|
return Err(Error::CommandFailed("exactly one argument must be provided".to_string()))
|
||||||
let key = Stdin::try_keystroke();
|
}
|
||||||
println!("waited {}", match key {
|
if let Ok(time) = args[0].parse::<u64>() {
|
||||||
Some(c) => c,
|
wait(time as f64);
|
||||||
None => '_',
|
println!("waited for {}s", time);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,12 +165,6 @@ async fn exec() -> Result<(), Error> {
|
|||||||
// not sure why this code was here but leaving it in case weird bugs happen so i remember to add it back if so
|
// not sure why this code was here but leaving it in case weird bugs happen so i remember to add it back if so
|
||||||
//interrupts::without_interrupts(|| {});
|
//interrupts::without_interrupts(|| {});
|
||||||
}
|
}
|
||||||
|
|
||||||
"print" => {
|
|
||||||
use crate::std::os::OS;
|
|
||||||
let x: String = OS.lock().version.clone();
|
|
||||||
println!("{}", x);
|
|
||||||
}
|
|
||||||
"switch" => {
|
"switch" => {
|
||||||
Screen::switch();
|
Screen::switch();
|
||||||
}
|
}
|
||||||
@@ -170,7 +180,7 @@ async fn exec() -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::UnknownCommand(
|
return Err(Error::UnknownCommand(
|
||||||
"command not yet implemented".to_string(),
|
cmd
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+125
-63
@@ -1,4 +1,4 @@
|
|||||||
use alloc::string::String;
|
use alloc::string::{String, ToString};
|
||||||
use alloc::{format, vec, vec::Vec, boxed::Box};
|
use alloc::{format, vec, vec::Vec, boxed::Box};
|
||||||
use alloc::borrow::ToOwned;
|
use alloc::borrow::ToOwned;
|
||||||
use core::arch::x86_64::_mm_test_all_ones;
|
use core::arch::x86_64::_mm_test_all_ones;
|
||||||
@@ -10,12 +10,19 @@ use crate::kernel::tasks::keyboard::KEYBOARD;
|
|||||||
use crossbeam_queue::SegQueue;
|
use crossbeam_queue::SegQueue;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use crate::kernel::render::{ColorCode, ScreenChar};
|
use crate::kernel::render::{ColorCode, ScreenChar};
|
||||||
use crate::{println};
|
use crate::{println, serial_println};
|
||||||
use crate::std::application::{Application, Error};
|
use crate::std::application::{Application, Error};
|
||||||
use crate::std::random::Random;
|
use crate::std::random::Random;
|
||||||
use crate::system::std::frame::ColouredElement;
|
use crate::system::std::frame::ColouredElement;
|
||||||
use super::super::lib::coords::{Line, Position, Direction};
|
use super::super::lib::coords::{Line, Position, Direction};
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum Gamemode {
|
||||||
|
SinglePlayer,
|
||||||
|
WithAI(u8, u8, u8), // number of ai, ai length, number of poi's
|
||||||
|
Uninitialised,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
enum Status {
|
enum Status {
|
||||||
@@ -29,7 +36,7 @@ pub struct Game {
|
|||||||
snakes: Vec<Snake>,
|
snakes: Vec<Snake>,
|
||||||
pois: Vec<Position>,
|
pois: Vec<Position>,
|
||||||
score: u8,
|
score: u8,
|
||||||
hardmode: bool,
|
gamemode: Gamemode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -41,36 +48,70 @@ impl Application for Game {
|
|||||||
snakes: Vec::new(),
|
snakes: Vec::new(),
|
||||||
pois: Vec::new(),
|
pois: Vec::new(),
|
||||||
score: 0,
|
score: 0,
|
||||||
hardmode: false,
|
gamemode: Gamemode::Uninitialised,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(&mut self, _: Vec<String>) -> Result<(), Error> {
|
async fn run(&mut self, args: Vec<String>) -> Result<(), Error> {
|
||||||
//Screen::application_mode();
|
|
||||||
|
|
||||||
|
let mut settings = [0, 0, 0]; // ai_count, snake_len, poi_count
|
||||||
|
|
||||||
// make the first poi
|
if args.len() == 0 {
|
||||||
|
self.gamemode == Gamemode::SinglePlayer;
|
||||||
self.snakes.push(Snake::player(0));
|
} else {
|
||||||
|
match args[0].as_str() {
|
||||||
for i in 0..5 {
|
"easy" => {
|
||||||
self.new_poi();
|
self.gamemode = Gamemode::SinglePlayer;
|
||||||
self.snakes.push(Snake::ai(i + 1));
|
},
|
||||||
|
"normal" => {
|
||||||
|
self.gamemode = Gamemode::WithAI(5, 5, 5);
|
||||||
|
},
|
||||||
|
"impossible" => {
|
||||||
|
self.gamemode = Gamemode::WithAI(10, 15, 5);
|
||||||
|
}
|
||||||
|
"chaos" => {
|
||||||
|
self.gamemode = Gamemode::WithAI(20, 15, 20);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.gamemode = Gamemode::SinglePlayer;
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sets up game based on difficulty
|
||||||
|
self.prepare();
|
||||||
|
|
||||||
|
// switch OS to application mode
|
||||||
|
Screen::application_mode();
|
||||||
// render the initial state of the screen.
|
// render the initial state of the screen.
|
||||||
self.render().map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?;
|
self.render().map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?;
|
||||||
|
|
||||||
// run the game
|
// run the game
|
||||||
self.gameloop().await?;
|
self.gameloop().await?;
|
||||||
|
|
||||||
// return to the terminal
|
// return to the terminal
|
||||||
//Screen::terminal_mode();
|
Screen::terminal_mode();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
|
fn prepare(&mut self) {
|
||||||
|
self.snakes.push(Snake::player(0, 3));
|
||||||
|
|
||||||
|
if let Gamemode::WithAI(ai_count, snake_len, poi_count) = self.gamemode {
|
||||||
|
for i in 0..ai_count {
|
||||||
|
self.snakes.push(Snake::ai(i as usize + 1, snake_len));
|
||||||
|
}
|
||||||
|
(0..poi_count).for_each(|_| self.new_poi());
|
||||||
|
} else {
|
||||||
|
self.new_poi()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn respawn_snakes(&mut self) {
|
||||||
|
if let Gamemode::WithAI(ai_count, snake_len, _poi) = self.gamemode {
|
||||||
|
self.snakes.push(Snake::ai(self.snakes.len() + 1, snake_len));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn gameloop(&mut self) -> Result<(), Error> { // main gameloop
|
async fn gameloop(&mut self) -> Result<(), Error> { // main gameloop
|
||||||
let mut all_points: Vec<Position>;
|
let mut all_points: Vec<Position>;
|
||||||
@@ -84,11 +125,14 @@ impl Game {
|
|||||||
|
|
||||||
for i in 0..length {
|
for i in 0..length {
|
||||||
let points: Vec<Position> = self.snakes.clone().into_iter().map(|s| s.tail).flatten().collect();
|
let points: Vec<Position> = self.snakes.clone().into_iter().map(|s| s.tail).flatten().collect();
|
||||||
let res = self.snakes[i].next(&self.pois, &points);
|
let res = self.snakes[i].next(&points, &self.pois);
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Status::Lost => {
|
Status::Lost => {
|
||||||
if !self.snakes[i].ai_controlled {
|
if self.snakes[i].ai_controlled {
|
||||||
|
self.snakes.remove(i);
|
||||||
|
self.respawn_snakes();
|
||||||
|
} else {
|
||||||
self.render_end_screen().map_err(|_| Error::ApplicationError(String::from("failed to render end screen")))?;
|
self.render_end_screen().map_err(|_| Error::ApplicationError(String::from("failed to render end screen")))?;
|
||||||
// loop triggers when game is lost
|
// loop triggers when game is lost
|
||||||
loop {
|
loop {
|
||||||
@@ -111,6 +155,7 @@ impl Game {
|
|||||||
Status::None => {},
|
Status::None => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.snakes.retain(|s| s.alive);
|
||||||
|
|
||||||
self.render().map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?;
|
self.render().map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?;
|
||||||
};
|
};
|
||||||
@@ -128,9 +173,18 @@ impl Game {
|
|||||||
|
|
||||||
fn render(&mut self) -> Result<(), ()> {
|
fn render(&mut self) -> Result<(), ()> {
|
||||||
let mut frame = vec![vec![ScreenChar::null(); 80]; 25];
|
let mut frame = vec![vec![ScreenChar::null(); 80]; 25];
|
||||||
self.snakes.clone().into_iter().map(|s| s.tail).flatten().for_each(|p| {
|
let mut curr_colour = ColorCode::new(Color::LightBlue, Color::Black);
|
||||||
frame[24 - p.y as usize][p.x as usize] = ScreenChar::new('@' as u8, ColorCode::new(Color::Cyan, Color::Black));
|
|
||||||
});
|
for s in self.snakes.clone() {
|
||||||
|
curr_colour = if s.ai_controlled {
|
||||||
|
ColorCode::new(Color::Cyan, Color::Black)
|
||||||
|
} else {
|
||||||
|
ColorCode::new(Color::LightGreen, Color::Black)
|
||||||
|
};
|
||||||
|
for point in s.tail.iter() {
|
||||||
|
frame[24 - point.y as usize][point.x as usize] = ScreenChar::new('@' as u8, curr_colour);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.pois.iter().for_each(|poi| {
|
self.pois.iter().for_each(|poi| {
|
||||||
frame[24 - poi.y as usize][poi.x as usize] = ScreenChar::new('o' as u8, ColorCode::new(Color::Red, Color::Black));
|
frame[24 - poi.y as usize][poi.x as usize] = ScreenChar::new('o' as u8, ColorCode::new(Color::Red, Color::Black));
|
||||||
@@ -138,7 +192,11 @@ impl Game {
|
|||||||
|
|
||||||
let literal = format!("snake go brr score: {}", self.score);
|
let literal = format!("snake go brr score: {}", self.score);
|
||||||
let msg = Game::centre_text(80, literal);
|
let msg = Game::centre_text(80, literal);
|
||||||
frame[1] = msg.chars().map(|c| ScreenChar::new(c as u8, ColorCode::new(Color::LightGreen, Color::Black))).collect();
|
msg.chars().enumerate().for_each(|(i, c)| {
|
||||||
|
if c != ' ' {
|
||||||
|
frame[1][i] = ScreenChar::new(c as u8, ColorCode::new(Color::LightGreen, Color::Black))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let mut elem = ColouredElement::generate(frame, (80, 25));
|
let mut elem = ColouredElement::generate(frame, (80, 25));
|
||||||
elem.render((0,0));
|
elem.render((0,0));
|
||||||
@@ -185,43 +243,45 @@ struct Snake {
|
|||||||
head: Position,
|
head: Position,
|
||||||
tail: Vec<Position>,
|
tail: Vec<Position>,
|
||||||
dir: Direction,
|
dir: Direction,
|
||||||
|
alive: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Snake {
|
impl Snake {
|
||||||
fn ai(id: usize) -> Self {
|
fn ai(id: usize, len: u8) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ai_controlled: true,
|
ai_controlled: true,
|
||||||
head: Position { x: 4 + 4*id as i64 * 2, y: 9 },
|
head: Position { x: 2 + 2*id as i64 * 2, y: 9 },
|
||||||
tail: (1..4).map(|p| Position { x: 4 + 4*id as i64, y: 5 + p}).collect(),
|
tail: (1..=len as i64).map(|p| Position { x: 2 + 2*id as i64, y: 5 + p}).collect(),
|
||||||
dir: Direction::PosY,
|
dir: Direction::Degrees0,
|
||||||
|
alive: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn player(id: usize) -> Self {
|
fn player(id: usize, len: u8) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ai_controlled: false,
|
ai_controlled: false,
|
||||||
head: Position { x: 4 + 4*id as i64, y: 9 },
|
head: Position { x: 2 + 2*id as i64, y: 9 },
|
||||||
tail: (1..4).map(|p| Position { x: 4 + 4*id as i64, y: 5 + p}).collect(),
|
tail: (1..=len as i64).map(|p| Position { x: 2 + 2*id as i64, y: 5 + p}).collect(),
|
||||||
dir: Direction::PosY,
|
dir: Direction::Degrees0,
|
||||||
|
alive: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(&mut self, points_of_interest: &Vec<Position>, tails: &Vec<Position>) -> Status { // returns (lose_condition, scored)
|
fn next(&mut self, tails: &Vec<Position>, points_of_interest: &Vec<Position>) -> Status { // returns (lose_condition, scored)
|
||||||
|
|
||||||
// uses pathing algorithm if ai else keyboard input if human
|
// uses pathing algorithm if ai else keyboard input if human
|
||||||
if self.ai_controlled {
|
if self.ai_controlled {
|
||||||
self.dir = PathFinder::decide(&self.head, points_of_interest, tails);
|
self.dir = PathFinder::decide(&self.head, tails, points_of_interest);
|
||||||
} else {
|
} else {
|
||||||
// if let Some(c) = Stdin::try_keystroke() {
|
if let Some(c) = Stdin::try_keystroke() {
|
||||||
// self.dir = match c {
|
self.dir = match c {
|
||||||
// 'w' => Direction::PosY,
|
'w' => Direction::Degrees0,
|
||||||
// 'a' => Direction::NegX,
|
'a' => Direction::Degrees270,
|
||||||
// 's' => Direction::NegY,
|
's' => Direction::Degrees180,
|
||||||
// 'd' => Direction::PosX,
|
'd' => Direction::Degrees90,
|
||||||
// 'x' => return Status::Exited,
|
'x' => return Status::Exited,
|
||||||
// _ => self.dir.clone(),
|
_ => self.dir.clone(),
|
||||||
// }
|
};
|
||||||
// }
|
}
|
||||||
self.dir = Direction::None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.dir != Direction::None {
|
if self.dir != Direction::None {
|
||||||
@@ -229,14 +289,15 @@ impl Snake {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match self.dir {
|
match self.dir {
|
||||||
Direction::PosY => self.head.y += 1,
|
Direction::Degrees0 => self.head.y += 1,
|
||||||
Direction::NegY => self.head.y -= 1,
|
Direction::Degrees180 => self.head.y -= 1,
|
||||||
Direction::NegX => self.head.x -= 1,
|
Direction::Degrees270 => self.head.x -= 1,
|
||||||
Direction::PosX => self.head.x += 1,
|
Direction::Degrees90 => self.head.x += 1,
|
||||||
Direction::None => {},
|
Direction::None => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.lose_condition(tails) {
|
if self.lose_condition(tails) {
|
||||||
|
self.alive = false;
|
||||||
self.tail.remove(0);
|
self.tail.remove(0);
|
||||||
return Status::Lost;
|
return Status::Lost;
|
||||||
}
|
}
|
||||||
@@ -266,7 +327,7 @@ struct PathFinder {}
|
|||||||
impl PathFinder {
|
impl PathFinder {
|
||||||
fn decide(head: &Position, tails: &Vec<Position>, pois: &Vec<Position>) -> Direction {
|
fn decide(head: &Position, tails: &Vec<Position>, pois: &Vec<Position>) -> Direction {
|
||||||
let nearest_poi = head.nearest(pois);
|
let nearest_poi = head.nearest(pois);
|
||||||
let rel_pos = head.distance(&nearest_poi);
|
let rel_pos = head.get_offset(&nearest_poi);
|
||||||
|
|
||||||
// check actions don't lose them the game
|
// check actions don't lose them the game
|
||||||
let mut possible_moves = Vec::new();
|
let mut possible_moves = Vec::new();
|
||||||
@@ -274,27 +335,28 @@ impl PathFinder {
|
|||||||
|
|
||||||
h = Position { x: head.x + 1, y: head.y };
|
h = Position { x: head.x + 1, y: head.y };
|
||||||
if !(PathFinder::check_bounds(&h) || PathFinder::check_collision(&h, &tails)) {
|
if !(PathFinder::check_bounds(&h) || PathFinder::check_collision(&h, &tails)) {
|
||||||
possible_moves.push(Direction::PosX);
|
possible_moves.push(Direction::Degrees90);
|
||||||
}
|
}
|
||||||
h = Position { x: head.x - 1, y: head.y };
|
h = Position { x: head.x - 1, y: head.y };
|
||||||
if !(PathFinder::check_bounds(&h) || PathFinder::check_collision(&h, &tails)) {
|
if !(PathFinder::check_bounds(&h) || PathFinder::check_collision(&h, &tails)) {
|
||||||
possible_moves.push(Direction::NegX);
|
possible_moves.push(Direction::Degrees270);
|
||||||
}
|
}
|
||||||
h = Position { x: head.x, y: head.y + 1 };
|
h = Position { x: head.x, y: head.y + 1 };
|
||||||
if !(PathFinder::check_bounds(&h) || PathFinder::check_collision(&h, &tails)) {
|
if !(PathFinder::check_bounds(&h) || PathFinder::check_collision(&h, &tails)) {
|
||||||
possible_moves.push(Direction::PosY);
|
possible_moves.push(Direction::Degrees0);
|
||||||
}
|
}
|
||||||
h = Position { x: head.x, y: head.y - 1 };
|
h = Position { x: head.x, y: head.y - 1 };
|
||||||
if !(PathFinder::check_bounds(&h) || PathFinder::check_collision(&h, &tails)) {
|
if !(PathFinder::check_bounds(&h) || PathFinder::check_collision(&h, &tails)) {
|
||||||
possible_moves.push(Direction::NegY);
|
possible_moves.push(Direction::Degrees180);
|
||||||
}
|
}
|
||||||
|
|
||||||
if possible_moves.is_empty() {
|
if possible_moves.is_empty() {
|
||||||
panic!("no possible moves");
|
// panic!("no possible moves"); // use for debugging if a snake cannot find a move for some reason
|
||||||
return Direction::None;
|
return Direction::Degrees90;
|
||||||
} else {
|
} else {
|
||||||
let optimal = PathFinder::optimal_move(head, &rel_pos, &possible_moves);
|
let optimal = PathFinder::optimal_move(head, &rel_pos, &possible_moves);
|
||||||
println!("{:?} {:?} {:?} {:?}", nearest_poi, rel_pos, head, optimal);
|
// serial_println!("{:?} {:?} {:?} {:?}", nearest_poi, rel_pos, head, optimal);
|
||||||
|
return optimal;
|
||||||
}
|
}
|
||||||
|
|
||||||
Direction::None
|
Direction::None
|
||||||
@@ -315,19 +377,19 @@ impl PathFinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if rel_pos.x < 0 {
|
if rel_pos.x < 0 {
|
||||||
optimal_moves[x_offset] = Direction::NegX;
|
optimal_moves[x_offset] = Direction::Degrees270;
|
||||||
optimal_moves[x_offset + 2] = Direction::PosX;
|
optimal_moves[x_offset + 2] = Direction::Degrees90;
|
||||||
} else {
|
} else {
|
||||||
optimal_moves[x_offset] = Direction::PosX;
|
optimal_moves[x_offset] = Direction::Degrees90;
|
||||||
optimal_moves[x_offset + 2] = Direction::NegX;
|
optimal_moves[x_offset + 2] = Direction::Degrees270;
|
||||||
}
|
}
|
||||||
|
|
||||||
if rel_pos.y < 0 {
|
if rel_pos.y < 0 {
|
||||||
optimal_moves[y_offset] = Direction::NegY;
|
optimal_moves[y_offset] = Direction::Degrees180;
|
||||||
optimal_moves[y_offset + 2] = Direction::PosY;
|
optimal_moves[y_offset + 2] = Direction::Degrees0;
|
||||||
} else {
|
} else {
|
||||||
optimal_moves[y_offset] = Direction::PosY;
|
optimal_moves[y_offset] = Direction::Degrees0;
|
||||||
optimal_moves[y_offset + 2] = Direction::NegY;
|
optimal_moves[y_offset + 2] = Direction::Degrees180;
|
||||||
}
|
}
|
||||||
//println!("moves: {:?}, optimal_moves: {:?}, rel_pos: {:?}", moves, optimal_moves, rel_pos);
|
//println!("moves: {:?}, optimal_moves: {:?}, rel_pos: {:?}", moves, optimal_moves, rel_pos);
|
||||||
for m in optimal_moves {
|
for m in optimal_moves {
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use alloc::string::String;
|
||||||
|
use alloc::vec;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use crate::kernel::render::ScreenChar;
|
||||||
|
use crate::{serial_print, serial_println};
|
||||||
|
use crate::std::application::{Application, Error};
|
||||||
|
use crate::std::io::Screen;
|
||||||
|
use crate::user::lib::coords::{Direction, Position, PositionReal};
|
||||||
|
use crate::user::lib::libgui::libgui_core::Pos;
|
||||||
|
|
||||||
|
|
||||||
|
pub(crate) struct TetrisEngine {
|
||||||
|
score: u32,
|
||||||
|
next: TetrisPiece,
|
||||||
|
completed_frame: [[ScreenChar; 80]; 25], // this frame does not contain falling blocks, only static ones
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Application for TetrisEngine {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
score: 0,
|
||||||
|
next: TetrisPiece::new(PieceType::OPiece),
|
||||||
|
completed_frame: [[ScreenChar::null(); 80]; 25],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn run(&mut self, args: Vec<String>) -> Result<(), Error> {
|
||||||
|
// setup:
|
||||||
|
Screen::application_mode();
|
||||||
|
|
||||||
|
let piece_type = PieceType::OPiece;
|
||||||
|
let mut piece = TetrisPiece::new(piece_type);
|
||||||
|
|
||||||
|
serial_println!("{:?}", piece.get_positions());
|
||||||
|
piece.rotate_right();
|
||||||
|
serial_println!("{:?}", piece.get_positions());
|
||||||
|
|
||||||
|
|
||||||
|
Screen::terminal_mode();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum PieceType {
|
||||||
|
OPiece,
|
||||||
|
IPiece,
|
||||||
|
JPiece,
|
||||||
|
LPiece,
|
||||||
|
SPiece,
|
||||||
|
ZPiece,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TetrisPiece {
|
||||||
|
type_: PieceType,
|
||||||
|
pos: Position,
|
||||||
|
rotation: Direction,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TetrisPiece {
|
||||||
|
fn new(type_: PieceType) -> Self {
|
||||||
|
Self {
|
||||||
|
type_,
|
||||||
|
pos: Position { x: 40, y: 30 },
|
||||||
|
rotation: Direction::Degrees0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn rotate_right(&mut self) {
|
||||||
|
self.rotation = match self.rotation {
|
||||||
|
Direction::Degrees90 => Direction::Degrees180,
|
||||||
|
Direction::Degrees180 => Direction::Degrees270,
|
||||||
|
Direction::Degrees270 => Direction::Degrees0,
|
||||||
|
Direction::Degrees0 => Direction::Degrees90,
|
||||||
|
Direction::None => panic!("direction should never be none in this application"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// function that maps the coordinates of the object.
|
||||||
|
fn get_positions(&self) -> Vec<Position> {
|
||||||
|
match self.type_ {
|
||||||
|
PieceType::OPiece => {
|
||||||
|
let positions = vec![
|
||||||
|
PositionReal { x: -0.5, y: -0.5 },
|
||||||
|
PositionReal { x: 0.5, y: -0.5 },
|
||||||
|
PositionReal { x: -0.5, y: 0.5 },
|
||||||
|
PositionReal { x: 0.5, y: 0.5 },
|
||||||
|
];
|
||||||
|
positions.into_iter().map(|p|
|
||||||
|
( p.rotate(self.rotation.clone()) + self.pos.clone().real() + PositionReal { x: -0.5, y: 0.5 } ).integer()
|
||||||
|
).collect::<Vec<Position>>()
|
||||||
|
}
|
||||||
|
_ => unimplemented!("E"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+220
-14
@@ -1,8 +1,8 @@
|
|||||||
use alloc::borrow::ToOwned;
|
use alloc::borrow::ToOwned;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
use libm::sqrt;
|
||||||
use crate::println;
|
use crate::println;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Line {
|
pub enum Line {
|
||||||
Vertical(i64),
|
Vertical(i64),
|
||||||
@@ -15,61 +15,267 @@ pub struct Position {
|
|||||||
pub y: i64,
|
pub y: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// a point represented with x and y coordinates.
|
||||||
impl Position {
|
impl Position {
|
||||||
|
|
||||||
|
/// checks if the point is on a given line
|
||||||
pub fn touches_line(&self, line: &Line) -> bool {
|
pub fn touches_line(&self, line: &Line) -> bool {
|
||||||
match line {
|
match line {
|
||||||
Line::Vertical(y) => self.y == *y,
|
Line::Vertical(y) => self.y as i64 == *y,
|
||||||
Line::Horizontal(x) => self.x == *x,
|
Line::Horizontal(x) => self.x as i64 == *x,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// checks if two points are equal and returns the x and y equality result for each
|
||||||
pub fn aligns(&self, other: &Position) -> (bool, bool) {
|
pub fn aligns(&self, other: &Position) -> (bool, bool) {
|
||||||
(self.x == other.x, self.y == other.y)
|
(self.x == other.x, self.y == other.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn distance(&self, other: &Position) -> Position {
|
/// calculates x + y distance between two points
|
||||||
|
pub fn get_offset(&self, other: &Position) -> Position {
|
||||||
Position {
|
Position {
|
||||||
x: other.x - self.x,
|
x: other.x - self.x,
|
||||||
y: other.y - self.y
|
y: other.y - self.y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn diagonal_distance(&self, other: &Position) -> i64 {
|
||||||
|
sqrt((self.x - other.x).pow(2) as f64 + (self.y - other.y).pow(2) as f64) as i64
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_usize(&self) -> (usize, usize) {
|
pub fn as_usize(&self) -> (usize, usize) {
|
||||||
(self.x as usize, self.y as usize)
|
(self.x as usize, self.y as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn magnitude(&self) -> i64 {
|
pub fn magnitude(&self) -> i64 {
|
||||||
(self.x.abs() + self.y.abs())
|
self.x.abs() + self.y.abs()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nearest(&self, points: &Vec<Position>) -> Position {
|
pub fn nearest(&self, points: &Vec<Position>) -> Position {
|
||||||
|
|
||||||
let mut points = points.clone();
|
let mut points = points.clone();
|
||||||
points.sort_by_key(|p| {
|
points.sort_by_key(|p| {
|
||||||
let p = self.distance(p);
|
let p = self.get_offset(p);
|
||||||
p.x.abs() + p.y.abs()
|
p.x.abs() + p.y.abs()
|
||||||
});
|
});
|
||||||
points.first().unwrap().to_owned()
|
points.first().unwrap().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn rotated_around(&self, angle: Direction, p: Position) -> Position { // rotates by an angle around a point
|
||||||
|
|
||||||
|
// gets coords relative to point to rotate around
|
||||||
|
let mut p_offset = self.get_offset(&p);
|
||||||
|
|
||||||
|
p_offset = match angle { // default angle is posy = 0 degrees and negy = 180
|
||||||
|
Direction::Degrees0 => Position {
|
||||||
|
x: p_offset.x,
|
||||||
|
y: p_offset.y,
|
||||||
|
},
|
||||||
|
Direction::Degrees90 => Position {
|
||||||
|
x: -p_offset.y,
|
||||||
|
y: p_offset.x,
|
||||||
|
},
|
||||||
|
Direction::Degrees180 => Position {
|
||||||
|
x: -p_offset.x,
|
||||||
|
y: -p_offset.y,
|
||||||
|
},
|
||||||
|
Direction::Degrees270 => Position {
|
||||||
|
x: p_offset.y,
|
||||||
|
y: -p_offset.x,
|
||||||
|
},
|
||||||
|
Direction::None => panic!("direction should never be none in this application"),
|
||||||
|
};
|
||||||
|
|
||||||
|
return p_offset + p;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotate(&self, angle: Direction) -> Position { // rotates by an angle around origin
|
||||||
|
match angle { // default angle is posy = 0 degrees and negy = 180
|
||||||
|
Direction::Degrees0 => Position {
|
||||||
|
x: self.x,
|
||||||
|
y: self.y,
|
||||||
|
},
|
||||||
|
Direction::Degrees90 => Position {
|
||||||
|
x: -self.y,
|
||||||
|
y: self.x,
|
||||||
|
},
|
||||||
|
Direction::Degrees180 => Position {
|
||||||
|
x: -self.x,
|
||||||
|
y: -self.y,
|
||||||
|
},
|
||||||
|
Direction::Degrees270 => Position {
|
||||||
|
x: self.y,
|
||||||
|
y: -self.x,
|
||||||
|
},
|
||||||
|
Direction::None => panic!("direction should never be none in this application"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn real(self) -> PositionReal {
|
||||||
|
PositionReal {
|
||||||
|
x: self.x as f64,
|
||||||
|
y: self.y as f64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl core::ops::Add for Position {
|
||||||
|
type Output = Position;
|
||||||
|
fn add(self, other: Position) -> Position {
|
||||||
|
Position {
|
||||||
|
x: self.x + other.x,
|
||||||
|
y: self.y + other.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// can be expressed as degrees relative to north (where north faces towards the top of the screen)
|
||||||
|
/// none variant used for when the value is missing / no value is decided.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
PosY,
|
Degrees0,
|
||||||
NegY,
|
Degrees180,
|
||||||
PosX,
|
Degrees90,
|
||||||
NegX,
|
Degrees270,
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Direction {
|
impl Direction {
|
||||||
pub fn rev(&self) -> Direction {
|
pub fn rev(&self) -> Direction {
|
||||||
match self {
|
match self {
|
||||||
Direction::PosY => Direction::NegY,
|
Direction::Degrees0 => Direction::Degrees180,
|
||||||
Direction::NegY => Direction::PosY,
|
Direction::Degrees180 => Direction::Degrees0,
|
||||||
Direction::PosX => Direction::NegX,
|
Direction::Degrees90 => Direction::Degrees270,
|
||||||
Direction::NegX => Direction::PosX,
|
Direction::Degrees270 => Direction::Degrees90,
|
||||||
Direction::None => Direction::None,
|
Direction::None => Direction::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct PositionReal {
|
||||||
|
pub x: f64,
|
||||||
|
pub y: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// a point represented with x and y coordinates.
|
||||||
|
impl PositionReal {
|
||||||
|
|
||||||
|
/// checks if the point is on a given line
|
||||||
|
pub fn touches_line(&self, line: &Line) -> bool {
|
||||||
|
match line {
|
||||||
|
Line::Vertical(y) => self.y as i64 == *y,
|
||||||
|
Line::Horizontal(x) => self.x as i64 == *x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// checks if two points are equal and returns the x and y equality result for each
|
||||||
|
pub fn aligns(&self, other: &PositionReal) -> (bool, bool) {
|
||||||
|
(self.x == other.x, self.y == other.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// calculates x + y distance between two points
|
||||||
|
pub fn get_offset(&self, other: &PositionReal) -> PositionReal {
|
||||||
|
PositionReal {
|
||||||
|
x: other.x - self.x,
|
||||||
|
y: other.y - self.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn diagonal_distance(&self, other: &PositionReal) -> f64 {
|
||||||
|
sqrt((self.x - other.x)*(self.x - other.x) + (self.y - other.y)* (self.y - other.y) as f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_usize(&self) -> (usize, usize) {
|
||||||
|
(self.x as usize, self.y as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn magnitude(&self) -> i64 {
|
||||||
|
let absx = if self.x >= 0.0 { self.x } else { -self.x };
|
||||||
|
let absy = if self.y >= 0.0 { self.y } else { -self.y };
|
||||||
|
(absx + absy) as i64
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nearest(&self, points: &Vec<PositionReal>) -> PositionReal {
|
||||||
|
|
||||||
|
let mut points = points.clone();
|
||||||
|
points.sort_by_key(|p| {
|
||||||
|
let p = self.get_offset(p);
|
||||||
|
let absx = if p.x >= 0.0 { p.x } else { -p.x };
|
||||||
|
let absy = if p.y >= 0.0 { p.y } else { -p.y };
|
||||||
|
(absx + absy) as i64
|
||||||
|
});
|
||||||
|
points.first().unwrap().to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn rotated_around(&self, angle: Direction, p: PositionReal) -> PositionReal { // rotates by an angle around a point
|
||||||
|
|
||||||
|
// gets coords relative to point to rotate around
|
||||||
|
let mut p_offset = self.get_offset(&p);
|
||||||
|
|
||||||
|
p_offset = match angle { // default angle is posy = 0 degrees and negy = 180
|
||||||
|
Direction::Degrees0 => PositionReal {
|
||||||
|
x: p_offset.x,
|
||||||
|
y: p_offset.y,
|
||||||
|
},
|
||||||
|
Direction::Degrees90 => PositionReal {
|
||||||
|
x: -p_offset.y,
|
||||||
|
y: p_offset.x,
|
||||||
|
},
|
||||||
|
Direction::Degrees180 => PositionReal {
|
||||||
|
x: -p_offset.x,
|
||||||
|
y: -p_offset.y,
|
||||||
|
},
|
||||||
|
Direction::Degrees270 => PositionReal {
|
||||||
|
x: p_offset.y,
|
||||||
|
y: -p_offset.x,
|
||||||
|
},
|
||||||
|
Direction::None => panic!("direction should never be none in this application"),
|
||||||
|
};
|
||||||
|
|
||||||
|
return p_offset + p;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotate(&self, angle: Direction) -> PositionReal { // rotates by an angle around origin
|
||||||
|
match angle { // default angle is posy = 0 degrees and negy = 180
|
||||||
|
Direction::Degrees0 => PositionReal {
|
||||||
|
x: self.x,
|
||||||
|
y: self.y,
|
||||||
|
},
|
||||||
|
Direction::Degrees90 => PositionReal {
|
||||||
|
x: -self.y,
|
||||||
|
y: self.x,
|
||||||
|
},
|
||||||
|
Direction::Degrees180 => PositionReal {
|
||||||
|
x: -self.x,
|
||||||
|
y: -self.y,
|
||||||
|
},
|
||||||
|
Direction::Degrees270 => PositionReal {
|
||||||
|
x: self.y,
|
||||||
|
y: -self.x,
|
||||||
|
},
|
||||||
|
Direction::None => panic!("direction should never be none in this application"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn integer(self) -> Position {
|
||||||
|
Position {
|
||||||
|
x: self.x as i64,
|
||||||
|
y: self.y as i64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::Add for PositionReal {
|
||||||
|
type Output = PositionReal;
|
||||||
|
fn add(self, other: PositionReal) -> PositionReal {
|
||||||
|
PositionReal {
|
||||||
|
x: self.x + other.x,
|
||||||
|
y: self.y + other.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user