Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 10 additions & 27 deletions examples/battleship/battlestate.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use std::{convert::TryInto, fmt::Display};
use std::convert::TryInto;
use turtle::rand::{choose, random_range};

use crate::{
Expand Down Expand Up @@ -37,28 +37,6 @@ pub struct BattleState {
pub ships_lost: u8,
}

impl Display for BattleState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let output = self
.ship_grid
.iter()
.map(|row| {
row.iter()
.map(|cell| match cell {
Cell::Carrier => 'C',
Cell::Battleship => 'B',
Cell::Cruiser => 'R',
Cell::Submarine => 'S',
Cell::Destroyer => 'D',
_ => '.',
})
.collect::<String>()
})
.collect::<Vec<_>>();
write!(f, "{}", output.join("\n"))
}
}

impl BattleState {
pub fn new() -> Self {
let (ships, ship_grid) = Self::random_ship_grid();
Expand All @@ -74,13 +52,18 @@ impl BattleState {
let attacked_cell = self.ship_grid.get(pos);
match attacked_cell {
Cell::Empty => AttackOutcome::Miss,
Cell::Carrier | Cell::Battleship | Cell::Cruiser | Cell::Submarine | Cell::Destroyer => {
Cell::Ship(_) => {
let standing_ship_parts = self.ship_grid.count(&attacked_cell);
match standing_ship_parts {
1 => {
// If the attack is on the last standing ship part,
// change all the Cells of the Ship to Destroyed
let lost_ship = self.ships[attacked_cell as usize];
let lost_ship = self
.ships
.iter()
.find(|ship| ship.kind.to_cell() == attacked_cell)
.copied()
.unwrap();
lost_ship
.coordinates()
.into_iter()
Expand All @@ -94,14 +77,14 @@ impl BattleState {
}
}
}
_ => unreachable!(),
Cell::Bombed | Cell::Missed | Cell::Destroyed | Cell::Unattacked => unreachable!(),
}
}
pub fn can_bomb(&self, pos: &(u8, u8)) -> bool {
match self.attack_grid.get(pos) {
Cell::Bombed | Cell::Destroyed | Cell::Missed => false,
Cell::Unattacked => true,
_ => unreachable!(),
Cell::Ship(_) | Cell::Empty => unreachable!(),
}
}
pub fn set_attack_outcome(&mut self, attacked_pos: &(u8, u8), outcome: AttackOutcome) {
Expand Down
12 changes: 6 additions & 6 deletions examples/battleship/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{grid::Cell, ship::ShipKind};
use turtle::Color;

use crate::grid::Cell;
pub struct Config {}

impl Config {
Expand Down Expand Up @@ -31,11 +31,11 @@ impl Config {

pub fn cell_color(cell: &Cell) -> Color {
match cell {
Cell::Carrier => Self::CARRIER_COLOR.into(),
Cell::Battleship => Self::BATTLESHIP_COLOR.into(),
Cell::Cruiser => Self::CRUISER_COLOR.into(),
Cell::Submarine => Self::SUBMARINE_COLOR.into(),
Cell::Destroyer => Self::DESTROYER_COLOR.into(),
Cell::Ship(ShipKind::Carrier) => Self::CARRIER_COLOR.into(),
Cell::Ship(ShipKind::Battleship) => Self::BATTLESHIP_COLOR.into(),
Cell::Ship(ShipKind::Cruiser) => Self::CRUISER_COLOR.into(),
Cell::Ship(ShipKind::Submarine) => Self::SUBMARINE_COLOR.into(),
Cell::Ship(ShipKind::Destroyer) => Self::DESTROYER_COLOR.into(),
Comment on lines +34 to +38
Copy link
Contributor

@enaut enaut Jul 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really necessary but sometimes it is more readable to nest matches in that case:

Cell::Ship(kind) => match kind {
            ShipKind::Carrier => Self::CARRIER_COLOR.into(),
            ShipKind::Battleship => Self::BATTLESHIP_COLOR.into(),
            ShipKind::Cruiser => Self::CRUISER_COLOR.into(),
            ShipKind::Submarine => Self::SUBMARINE_COLOR.into(),
            ShipKind::Destroyer => Self::DESTROYER_COLOR.into()
}

But as I said in this case its perfectly readable so no need to change.

Cell::Empty => Self::EMPTY_COLOR.into(),
Cell::Unattacked => Self::UNATTACKED_COLOR.into(),
Cell::Missed => Self::MISSED_COLOR.into(),
Expand Down
4 changes: 2 additions & 2 deletions examples/battleship/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,14 @@ impl Game {
// TODO: Replace poll_event with blocking next_event after Events API is stabilized
// https://github.com/sunjay/turtle/issues/178
while let Some(event) = drawing.poll_event() {
use Key::{DownArrow, LeftArrow, Return, RightArrow, UpArrow};
use Key::{DownArrow, LeftArrow, Return, RightArrow, Space, UpArrow};
if let Event::Key(key, PressedState::Pressed) = event {
match key {
LeftArrow => crosshair.move_left(self),
RightArrow => crosshair.move_right(self),
UpArrow => crosshair.move_up(self),
DownArrow => crosshair.move_down(self),
Return => {
Return | Space => {
if let Some(pos) = crosshair.try_bomb() {
return pos;
}
Expand Down
44 changes: 31 additions & 13 deletions examples/battleship/grid.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
use crate::ship::ShipKind;
use std::ops::Deref;
use std::{fmt::Display, ops::Deref};

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Cell {
Carrier,
Battleship,
Cruiser,
Submarine,
Destroyer,
Ship(ShipKind),
/// clear cell on ShipGrid
Empty,
/// clear cell on AttackGrid
Expand All @@ -18,18 +14,28 @@ pub enum Cell {
Destroyed,
}

impl ShipKind {
pub fn to_cell(self) -> Cell {
impl Display for Cell {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ShipKind::Carrier => Cell::Carrier,
ShipKind::Battleship => Cell::Battleship,
ShipKind::Cruiser => Cell::Cruiser,
ShipKind::Submarine => Cell::Submarine,
ShipKind::Destroyer => Cell::Destroyer,
Cell::Ship(ShipKind::Carrier) => write!(f, "C"),
Cell::Ship(ShipKind::Battleship) => write!(f, "B"),
Cell::Ship(ShipKind::Cruiser) => write!(f, "R"),
Cell::Ship(ShipKind::Submarine) => write!(f, "S"),
Cell::Ship(ShipKind::Destroyer) => write!(f, "D"),
Cell::Empty | Cell::Unattacked => write!(f, "."),
Cell::Missed => write!(f, ","),
Cell::Bombed => write!(f, "*"),
Cell::Destroyed => write!(f, "#"),
}
}
}

impl ShipKind {
pub fn to_cell(self) -> Cell {
Cell::Ship(self)
}
}

#[derive(Debug, Copy, Clone)]
pub struct Grid([[Cell; 10]; 10]);

Expand All @@ -40,6 +46,18 @@ impl Deref for Grid {
}
}

impl Display for Grid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for i in 0..10 {
for j in 0..10 {
write!(f, "{}", self.get(&(j, i)))?
}
writeln!(f)?
}
Ok(())
}
}

impl Grid {
pub fn new(cell: Cell) -> Self {
Self { 0: [[cell; 10]; 10] }
Expand Down
9 changes: 5 additions & 4 deletions examples/battleship/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,20 @@
//! Whenever a shot was fired the player gets a feedback: either miss (•) or hit (a red ⚫).
//! That feedback enables some optimization strategies - to guess strategically optimal.
//!
//! You can use the arrow keys (←, ↑, ↓, →) to move the cross-hair in AttackGrid and press the `Enter ⏎` key to attack.
//! You can use the arrow keys (←, ↑, ↓, →) to move the cross-hair in AttackGrid
//! and press the `Enter ⏎` or Space key to attack.
//!
//! To play in single player mode, you can pass `bot` as an argument to the program
//!
//!
//! $> ./battleship bot
//! $> cargo run --features unstable --example battleship bot # From within the turtle source-code.
//!
//! To play in multiplayer mode, one player needs to act as the server and the other as client.
//! To act as a server, run the program without any arguments.
//!
//!
//! $> ./battleship # No arguments
//! $> cargo run --features unstable --example battleship # From within the turtle source-code.
//!
//!
//! This will output something like "Listening on port: <PORT>, Waiting for connection..".
//! As soon as an opponent connects the game starts.
//!
Expand Down