idk
idk, did some rewriting of the snake game logic to add multiple snakes / a hardmode
This commit is contained in:
+1
-1
@@ -11,7 +11,7 @@ test-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "st
|
|||||||
test-success-exit-code = 33
|
test-success-exit-code = 33
|
||||||
test-timeout = 30
|
test-timeout = 30
|
||||||
|
|
||||||
run-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "stdio"]
|
run-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "stdio", "-accel", "kvm"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bootloader= { version = "0.9.23", features = ["map_physical_memory"] }
|
bootloader= { version = "0.9.23", features = ["map_physical_memory"] }
|
||||||
|
|||||||
+191
-85
@@ -1,5 +1,6 @@
|
|||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::{format, vec, vec::Vec, boxed::Box};
|
use alloc::{format, vec, vec::Vec, boxed::Box};
|
||||||
|
use core::arch::x86_64::_mm_test_all_ones;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use crate::std::io::{Color, Screen, Stdin};
|
use crate::std::io::{Color, Screen, Stdin};
|
||||||
use crate::std::time;
|
use crate::std::time;
|
||||||
@@ -16,113 +17,132 @@ struct Point {
|
|||||||
y: i8,
|
y: i8,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Game {
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
snake: SegQueue<Point>,
|
struct Position {
|
||||||
head: Point,
|
x: i8,
|
||||||
poi: Point,
|
y: i8,
|
||||||
mv: char,
|
dir: Direction,
|
||||||
score: u8
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
enum Direction {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
enum Status {
|
||||||
|
Scored,
|
||||||
|
Lost,
|
||||||
|
Exited,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Game {
|
||||||
|
snakes: Vec<Snake>,
|
||||||
|
pois: Vec<Point>,
|
||||||
|
score: u8,
|
||||||
|
hardmode: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Application for Game {
|
impl Application for Game {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
snake: SegQueue::new(),
|
snakes: Vec::new(),
|
||||||
head: Point { x: 5, y: 5 },
|
pois: Vec::new(),
|
||||||
poi: Point { x: 0, y: 0 },
|
score: 0,
|
||||||
mv: ' ',
|
hardmode: false,
|
||||||
score: 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(&mut self, _: Vec<String>) -> Result<(), Error> {
|
async fn run(&mut self, _: Vec<String>) -> Result<(), Error> {
|
||||||
Screen::application_mode();
|
Screen::application_mode();
|
||||||
let clone = self.clone_snake();
|
|
||||||
self.render(clone).map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?;
|
|
||||||
|
|
||||||
|
// render the initial state of the screen.
|
||||||
|
self.render().map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?;
|
||||||
|
|
||||||
(5..=7).for_each(|x| {
|
// make the first poi
|
||||||
self.snake.push(Point { x, y: 5 });
|
for i in 0..5 {
|
||||||
});
|
|
||||||
self.head = Point { x: 7, y: 5 };
|
|
||||||
self.new_poi();
|
self.new_poi();
|
||||||
|
|
||||||
'gameloop: loop {
|
|
||||||
|
|
||||||
time::wait(0.2);
|
|
||||||
|
|
||||||
if let Some(c) = Stdin::try_keystroke() {
|
|
||||||
self.mv = c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//self.mv = Stdin::keystroke().await;
|
// run the game
|
||||||
|
self.gameloop().await?;
|
||||||
match self.mv {
|
|
||||||
'w' => self.head.y -= 1,
|
|
||||||
'a' => self.head.x -= 1,
|
|
||||||
's' => self.head.y += 1,
|
|
||||||
'd' => self.head.x += 1,
|
|
||||||
'x' => break,
|
|
||||||
_ => continue,
|
|
||||||
}
|
|
||||||
|
|
||||||
self.snake.push(Point { x: self.head.x, y: self.head.y }); // new head added
|
|
||||||
|
|
||||||
if self.head == self.poi {
|
|
||||||
self.new_poi();
|
|
||||||
self.score += 1
|
|
||||||
} else {
|
|
||||||
self.snake.pop().unwrap(); // tail removed if score does not increase
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.lose_condition() {
|
|
||||||
self.render_end_screen().map_err(|_| Error::ApplicationError(String::from("failed to render game over screen")))?;
|
|
||||||
while let chr = KEYBOARD.lock().get_keystroke().await {
|
|
||||||
match chr {
|
|
||||||
'x' => break 'gameloop,
|
|
||||||
_ => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let clone = self.clone_snake();
|
|
||||||
self.render(clone).map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// return to the terminal
|
||||||
Screen::terminal_mode();
|
Screen::terminal_mode();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
|
|
||||||
|
async fn gameloop(&mut self) -> Result<(), Error> { // main gameloop
|
||||||
|
let mut all_points: Vec<Point>;
|
||||||
|
|
||||||
|
'gameloop: loop {
|
||||||
|
|
||||||
|
time::wait(0.1);
|
||||||
|
|
||||||
|
let res = self.snakes.iter_mut().map(|s| s.next(&self.pois, &self.snakes)).collect::<Vec<Status>>();
|
||||||
|
if res.contains(&Status::Lost) {
|
||||||
|
self.render_end_screen().map_err(|_| Error::ApplicationError(String::from("failed to render end screen")))?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match Stdin::keystroke().await {
|
||||||
|
'x' => break 'gameloop,
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if res.contains(&Status::Exited) {
|
||||||
|
break 'gameloop;
|
||||||
|
} else if res.contains(&Status::Scored) {
|
||||||
|
self.score += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn new_poi(&mut self) {
|
fn new_poi(&mut self) {
|
||||||
self.poi = Point { x: Random::int(3, 76) as i8, y: Random::int(3, 21) as i8 }
|
self.pois.push(Point { x: Random::int(3, 76) as i8, y: Random::int(3, 21) as i8 });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, snake: Vec<Point>) -> Result<(), ()> {
|
fn replace_poi(&mut self, poi: &Point) {
|
||||||
let mut frame = vec![vec![ScreenChar::null(); 80]; 25];
|
self.pois.remove(self.pois.iter().position(|p| p == poi).unwrap());
|
||||||
snake.into_iter().for_each(|p| {
|
self.new_poi();
|
||||||
frame[p.y as usize][p.x as usize] = ScreenChar::new('@' as u8, ColorCode::new(Color::Cyan, Color::Black));
|
|
||||||
});
|
|
||||||
|
|
||||||
frame[self.poi.y as usize][self.poi.x as usize] = ScreenChar::new('o' as u8, ColorCode::new(Color::Red, Color::Black));
|
|
||||||
let literal = format!("snake go brr score: {}", self.score);
|
|
||||||
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();
|
|
||||||
|
|
||||||
let mut elem = ColouredElement::generate(frame, (80, 25));
|
|
||||||
elem.render((0,0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lose_condition(&mut self) -> bool {
|
fn render(&mut self) -> Result<(), ()> {
|
||||||
let cloned = self.clone_snake();
|
// let mut frame = vec![vec![ScreenChar::null(); 80]; 25];
|
||||||
let snake_overlaps = (1..cloned.len()).any(|i| cloned[i..].contains(&cloned[i - 1])); // checks if any part of the snake overlaps itself
|
// snake.into_iter().for_each(|p| {
|
||||||
let out_of_bounds = cloned.iter().filter(|p| p.x < 0 || p.y < 0 || p.x > 79 || p.y > 24).count() > 0; // checks if the snake goes out of bounds
|
// frame[p.y as usize][p.x as usize] = ScreenChar::new('@' as u8, ColorCode::new(Color::Cyan, Color::Black));
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// frame[self.poi.y as usize][self.poi.x as usize] = ScreenChar::new('o' as u8, ColorCode::new(Color::Red, Color::Black));
|
||||||
|
// let literal = format!("snake go brr score: {}", self.score);
|
||||||
|
// 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();
|
||||||
|
//
|
||||||
|
// let mut elem = ColouredElement::generate(frame, (80, 25));
|
||||||
|
// elem.render((0,0))
|
||||||
|
|
||||||
snake_overlaps || out_of_bounds
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn centre_text(dims: usize, text: String) -> String { // centres text in a string of whitespace of a given length
|
fn centre_text(dims: usize, text: String) -> String { // centres text in a string of whitespace of a given length
|
||||||
let max_pad = dims / 2;
|
let max_pad = dims / 2;
|
||||||
let mut msg = String::new();
|
let mut msg = String::new();
|
||||||
@@ -143,19 +163,105 @@ impl Game {
|
|||||||
let mut elem = ColouredElement::generate(frame, (80, 25));
|
let mut elem = ColouredElement::generate(frame, (80, 25));
|
||||||
elem.render((0,0))
|
elem.render((0,0))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn clone_snake(&mut self) -> Vec<Point> {
|
|
||||||
let mut cloned= Vec::new();
|
|
||||||
let mut snake = SegQueue::new();
|
|
||||||
while !self.snake.is_empty() {
|
|
||||||
let item = self.snake.pop().unwrap();
|
|
||||||
cloned.push(item.clone());
|
|
||||||
snake.push(item);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct Snake {
|
||||||
|
ai_controlled: bool,
|
||||||
|
head: Point,
|
||||||
|
tail: Vec<Point>,
|
||||||
|
dir: Direction,
|
||||||
}
|
}
|
||||||
self.snake = snake;
|
|
||||||
cloned
|
impl Snake {
|
||||||
|
fn ai(id: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
ai_controlled: true,
|
||||||
|
head: Point { x: 1 + id as i8 * 2, y: 1 },
|
||||||
|
tail: Vec::new(),
|
||||||
|
dir: Direction::Up
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn player(id: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
ai_controlled: false,
|
||||||
|
head: Point { x: 1 + id as i8 * 2, y: 1 },
|
||||||
|
tail: Vec::new(),
|
||||||
|
dir: Direction::Up,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self, points_of_interest: &Vec<Point>, tails: &Vec<Snake>) -> Status { // returns (lose_condition, scored)
|
||||||
|
|
||||||
|
// uses pathing algorithm if ai else keyboard input if human
|
||||||
|
if self.ai_controlled {
|
||||||
|
self.dir = self.decide_dir();
|
||||||
|
} else {
|
||||||
|
if let Some(c) = Stdin::try_keystroke() {
|
||||||
|
self.dir = match c {
|
||||||
|
'w' => Direction::Up,
|
||||||
|
'a' => Direction::Left,
|
||||||
|
's' => Direction::Down,
|
||||||
|
'd' => Direction::Right,
|
||||||
|
'x' => return Status::Exited,
|
||||||
|
_ => self.dir.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tail.push(self.head.clone());
|
||||||
|
|
||||||
|
match self.dir {
|
||||||
|
Direction::Up => self.head.y -= 1,
|
||||||
|
Direction::Down => self.head.y += 1,
|
||||||
|
Direction::Left => self.head.x -= 1,
|
||||||
|
Direction::Right => self.head.x += 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.lose_condition(tails) {
|
||||||
|
if !self.ai_controlled {
|
||||||
|
return Status::Lost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if points_of_interest.contains(&self.head) {
|
||||||
|
if !self.ai_controlled {
|
||||||
|
Status::Scored
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.tail.remove(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Status::None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decide_dir(&mut self) -> Direction {
|
||||||
|
unimplemented!() // implement a basic pathfinding or random movement algorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lose_condition(&mut self, tails: &Vec<Snake>) -> bool { // where tails includes the tail of every other snake
|
||||||
|
let p = self.head.clone();
|
||||||
|
|
||||||
|
let tails: Vec<Point> = tails.iter().map(|t| t.tail.flatten()).collect();
|
||||||
|
|
||||||
|
let snake_overlaps = tails.contains(&self.head); // checks if any part of the snake overlaps itself
|
||||||
|
let out_of_bounds = p.x < 0 || p.y < 0 || p.x > 79 || p.y > 24; // checks if the snake goes out of bounds
|
||||||
|
|
||||||
|
snake_overlaps || out_of_bounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn round_up(n: f64) -> usize {
|
fn round_up(n: f64) -> usize {
|
||||||
(n + 0.99) as usize
|
(n + 0.99) as usize
|
||||||
|
|||||||
@@ -0,0 +1,290 @@
|
|||||||
|
use alloc::string::String;
|
||||||
|
use alloc::{format, vec, vec::Vec, boxed::Box};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use crate::std::io::{Color, Screen, Stdin};
|
||||||
|
use crate::std::time;
|
||||||
|
use crate::kernel::tasks::keyboard::KEYBOARD;
|
||||||
|
use crossbeam_queue::SegQueue;
|
||||||
|
use crate::kernel::render::{ColorCode, ScreenChar};
|
||||||
|
use crate::std::application::{Application, Error};
|
||||||
|
use crate::std::random::Random;
|
||||||
|
use crate::system::std::frame::ColouredElement;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
struct Point {
|
||||||
|
x: i8,
|
||||||
|
y: i8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
struct Position {
|
||||||
|
x: i8,
|
||||||
|
y: i8,
|
||||||
|
dir: Direction,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
enum Direction {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Status {
|
||||||
|
Scored,
|
||||||
|
Lost,
|
||||||
|
Exited,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Game {
|
||||||
|
snake: SegQueue<Point>,
|
||||||
|
head: Point,
|
||||||
|
poi: Point,
|
||||||
|
mv: char,
|
||||||
|
score: u8,
|
||||||
|
hardmode: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
snake.rs
|
||||||
|
#[async_trait]
|
||||||
|
impl Application for Game {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
snake: SegQueue::new(),
|
||||||
|
head: Point { x: 5, y: 5 },
|
||||||
|
poi: Point { x: 0, y: 0 },
|
||||||
|
mv: ' ',
|
||||||
|
score: 0,
|
||||||
|
hardmode: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(&mut self, _: Vec<String>) -> Result<(), Error> {
|
||||||
|
Screen::application_mode();
|
||||||
|
let clone = self.clone_snake();
|
||||||
|
|
||||||
|
// render the initial state of the screen.
|
||||||
|
self.render(clone).map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?;
|
||||||
|
|
||||||
|
|
||||||
|
(5..=7).for_each(|x| {
|
||||||
|
self.snake.push(Point { x, y: 5 });
|
||||||
|
});
|
||||||
|
self.head = Point { x: 7, y: 5 };
|
||||||
|
self.new_poi();
|
||||||
|
|
||||||
|
self.gameloop().await?;
|
||||||
|
|
||||||
|
Screen::terminal_mode();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Game {
|
||||||
|
|
||||||
|
async fn gameloop(&mut self) -> Result<(), Error> { // main gameloop
|
||||||
|
'gameloop: loop {
|
||||||
|
|
||||||
|
time::wait(0.1);
|
||||||
|
|
||||||
|
if let Some(c) = Stdin::try_keystroke() {
|
||||||
|
self.mv = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
//self.mv = Stdin::keystroke().await;
|
||||||
|
|
||||||
|
match self.mv {
|
||||||
|
'w' => self.head.y -= 1,
|
||||||
|
'a' => self.head.x -= 1,
|
||||||
|
's' => self.head.y += 1,
|
||||||
|
'd' => self.head.x += 1,
|
||||||
|
'x' => break,
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.snake.push(Point { x: self.head.x, y: self.head.y }); // new head added
|
||||||
|
|
||||||
|
if self.head == self.poi {
|
||||||
|
self.new_poi();
|
||||||
|
self.score += 1
|
||||||
|
} else {
|
||||||
|
self.snake.pop().unwrap(); // tail removed if score does not increase
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.lose_condition() {
|
||||||
|
self.render_end_screen().map_err(|_| Error::ApplicationError(String::from("failed to render game over screen")))?;
|
||||||
|
while let chr = KEYBOARD.lock().get_keystroke().await {
|
||||||
|
match chr {
|
||||||
|
'x' => break 'gameloop,
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let clone = self.clone_snake();
|
||||||
|
self.render(clone).map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?;
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_poi(&mut self) {
|
||||||
|
self.poi = Point { x: Random::int(3, 76) as i8, y: Random::int(3, 21) as i8 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, snake: Vec<Point>) -> Result<(), ()> {
|
||||||
|
let mut frame = vec![vec![ScreenChar::null(); 80]; 25];
|
||||||
|
snake.into_iter().for_each(|p| {
|
||||||
|
frame[p.y as usize][p.x as usize] = ScreenChar::new('@' as u8, ColorCode::new(Color::Cyan, Color::Black));
|
||||||
|
});
|
||||||
|
|
||||||
|
frame[self.poi.y as usize][self.poi.x as usize] = ScreenChar::new('o' as u8, ColorCode::new(Color::Red, Color::Black));
|
||||||
|
let literal = format!("snake go brr score: {}", self.score);
|
||||||
|
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();
|
||||||
|
|
||||||
|
let mut elem = ColouredElement::generate(frame, (80, 25));
|
||||||
|
elem.render((0,0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lose_condition(&mut self) -> bool {
|
||||||
|
let cloned = self.clone_snake();
|
||||||
|
let snake_overlaps = (1..cloned.len()).any(|i| cloned[i..].contains(&cloned[i - 1])); // checks if any part of the snake overlaps itself
|
||||||
|
let out_of_bounds = cloned.iter().filter(|p| p.x < 0 || p.y < 0 || p.x > 79 || p.y > 24).count() > 0; // checks if the snake goes out of bounds
|
||||||
|
|
||||||
|
snake_overlaps || out_of_bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn centre_text(dims: usize, text: String) -> String { // centres text in a string of whitespace of a given length
|
||||||
|
let max_pad = dims / 2;
|
||||||
|
let mut msg = String::new();
|
||||||
|
msg.push_str(" ".repeat(max_pad - round_up(text.len() as f64 / 2.0)).as_str());
|
||||||
|
msg.push_str(text.as_str());
|
||||||
|
msg.push_str(" ".repeat(max_pad - round_down(text.len() as f64 / 2.0 + 0.51)).as_str());
|
||||||
|
msg
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_end_screen(&mut self) -> Result<(), ()> {
|
||||||
|
let mut frame = vec![vec![ScreenChar::null(); 80]; 25];
|
||||||
|
|
||||||
|
frame[10] = Game::centre_text(80, String::from("u lost")).chars().map(|c| ScreenChar::new(c as u8, ColorCode::new(Color::Red, Color::Black))).collect();
|
||||||
|
frame[12] = Game::centre_text(80, String::from(format!("ur score was {}", self.score))).chars().map(|c| ScreenChar::new(c as u8, ColorCode::new(Color::LightGreen, Color::Black))).collect();
|
||||||
|
frame[14] = Game::centre_text(80, String::from("L bozo")).chars().map(|c| ScreenChar::new(c as u8, ColorCode::new(Color::Red, Color::Black))).collect();
|
||||||
|
|
||||||
|
|
||||||
|
let mut elem = ColouredElement::generate(frame, (80, 25));
|
||||||
|
elem.render((0,0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_snake(&mut self) -> Vec<Point> {
|
||||||
|
let mut cloned= Vec::new();
|
||||||
|
let mut snake = SegQueue::new();
|
||||||
|
while !self.snake.is_empty() {
|
||||||
|
let item = self.snake.pop().unwrap();
|
||||||
|
cloned.push(item.clone());
|
||||||
|
snake.push(item);
|
||||||
|
}
|
||||||
|
self.snake = snake;
|
||||||
|
cloned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct Snake {
|
||||||
|
ai_controlled: bool,
|
||||||
|
head: Point,
|
||||||
|
tail: Vec<Point>,
|
||||||
|
dir: Direction,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Snake {
|
||||||
|
fn ai(id: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
ai_controlled: true,
|
||||||
|
head: Point { x: 1 + id as i8 * 2, y: 1 },
|
||||||
|
tail: Vec::new(),
|
||||||
|
dir: Direction::Up
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn player(id: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
ai_controlled: false,
|
||||||
|
head: Point { x: 1 + id as i8 * 2, y: 1 },
|
||||||
|
tail: Vec::new(),
|
||||||
|
dir: Direction::Up,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self, points_of_interest: &Vec<Point>, tails: &Vec<Point>) -> Status { // returns (lose_condition, scored)
|
||||||
|
|
||||||
|
// uses pathing algorithm if ai else keyboard input if human
|
||||||
|
if self.ai_controlled {
|
||||||
|
self.dir = self.decide_dir();
|
||||||
|
} else {
|
||||||
|
if let Some(c) = Stdin::try_keystroke() {
|
||||||
|
self.dir = match c {
|
||||||
|
'w' => Direction::Up,
|
||||||
|
'a' => Direction::Left,
|
||||||
|
's' => Direction::Down,
|
||||||
|
'd' => Direction::Right,
|
||||||
|
'x' => return Status::Exited,
|
||||||
|
_ => self.dir.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tail.push(self.head.clone());
|
||||||
|
|
||||||
|
match self.dir {
|
||||||
|
Direction::Up => self.head.y -= 1,
|
||||||
|
Direction::Down => self.head.y += 1,
|
||||||
|
Direction::Left => self.head.x -= 1,
|
||||||
|
Direction::Right => self.head.x += 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.lose_condition(tails) {
|
||||||
|
return Status::Lost;
|
||||||
|
}
|
||||||
|
|
||||||
|
if points_of_interest.contains(&self.head) {
|
||||||
|
Status::Scored
|
||||||
|
} else {
|
||||||
|
self.tail.remove(0);
|
||||||
|
Status::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decide_dir(&mut self) -> Direction {
|
||||||
|
unimplemented!() // implement a basic pathfinding or random movement algorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lose_condition(&mut self, tails: &Vec<Point>) -> bool { // where tails includes the tail of every other snake
|
||||||
|
let p = self.head.clone();
|
||||||
|
|
||||||
|
let snake_overlaps = tails.contains(&self.head); // checks if any part of the snake overlaps itself
|
||||||
|
let out_of_bounds = p.x < 0 || p.y < 0 || p.x > 79 || p.y > 24; // checks if the snake goes out of bounds
|
||||||
|
|
||||||
|
snake_overlaps || out_of_bounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn round_up(n: f64) -> usize {
|
||||||
|
(n + 0.99) as usize
|
||||||
|
}
|
||||||
|
fn round_down(n: f64) -> usize {
|
||||||
|
n as usize
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user