Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
115 changes: 58 additions & 57 deletions src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,49 @@ use models::coordinate::Coordinate;
use placement_error::PlacementError;
use rules;
use models::tile::Tile;

use standard_tile_factory::StandardTileFactory;
use tile_factory::TileFactory;

type Result<T> = ::std::result::Result<T, Vec<PlacementError>>;

pub type TileMap = HashMap<Coordinate, (Tile, Orientation)>;

#[derive(Debug, Clone, PartialEq)]
pub struct Engine {
tiles: TileMap,
board: TileMap,
tile_pool: Vec<Tile>,
}

impl Engine {
pub fn new() -> Self {
let mut tiles = HashMap::new();
let tile_pool = StandardTileFactory::get_tile_pool();
Self::new_with_tile_pool(tile_pool)
}

tiles.insert([0, 0].into(), (Tile::new(), Orientation::Up));
pub fn new_with_tile_pool(tile_pool: Vec<Tile>) -> Self {
let tiles = HashMap::new();

Engine { tiles: tiles }
Engine {
board: tiles,
tile_pool: tile_pool,
}
}

// This will avoid tiles being ever mutable.
pub fn get_tiles(&self) -> &TileMap {
&self.tiles
// This will avoid tiles ever being mutable.
pub fn get_board(&self) -> &TileMap {
&self.board
}

pub fn place_next<T: Into<Coordinate>>(&self,
coordinate: T,
orientation: Orientation)
-> Result<Engine> {
let next_tile = Tile::new();

self.place(next_tile, coordinate, orientation)
if self.tile_pool.len() >= 1 {
let next_tile = self.tile_pool[0].clone();
self.place(next_tile, coordinate, orientation)
} else {
Err(vec![PlacementError::PoolOutOfTiles])
}
}

fn place<T: Into<Coordinate>>(&self,
Expand All @@ -48,18 +59,27 @@ impl Engine {
-> Result<Engine> {
let coordinate: Coordinate = coordinate.into();

let broken_rules = rules::check(self.get_tiles(), &tile, &coordinate, &orientation);
let broken_rules = rules::check(self.get_board(), &tile, &coordinate, &orientation);

if broken_rules.is_err() {
return Err(broken_rules.unwrap_err());
}

let mut new_tiles = self.tiles.clone();
new_tiles.insert(coordinate, (tile, orientation));
let new_engine = Engine { tiles: new_tiles };
let mut new_board = self.board.clone();
new_board.insert(coordinate, (tile, orientation));
let (_, new_tile_pool) = self.tile_pool.split_first().unwrap();

let new_engine = Engine {
board: new_board,
tile_pool: new_tile_pool.to_vec(),
};

Ok(new_engine)
}

pub fn get_next_tile(&self) -> Option<Tile> {
Some(Tile::new())
}
}

#[cfg(test)]
Expand All @@ -68,80 +88,61 @@ mod test {

#[test]
fn test_can_place_tile() {
let engine = Engine::new();
let tile = Tile::new();
let engine = Engine::new_with_tile_pool(vec![tile.clone()]);

let new_engine = engine.place(tile, [0, 1], Orientation::Up);

assert!(new_engine.is_ok());
}

#[test]
fn test_can_place_next_tile() {
let engine = Engine::new();

let new_engine = engine.place_next([0, 1], Orientation::Up);
let place_result = engine.place(tile, [0, 1], Orientation::Up);

assert!(new_engine.is_ok());
assert!(place_result.is_ok());
}

#[test]
fn test_placing_not_adjectent_returns_error() {
let engine = Engine::new();

let new_engine = engine.place_next([0, 99], Orientation::Up);

assert!(new_engine.is_err());
}
let place_result = engine.place_next([0, 99], Orientation::Up);

#[test]
fn test_new_has_center_tile() {
assert!(Engine::new()
.get_tiles()
.contains_key(&Coordinate::from([0, 0])));
}

#[test]
fn test_new_only_has_single_tile() {
assert_eq!(1, Engine::new().get_tiles().len());
assert!(place_result.is_err());
}

#[test]
fn test_coord_exists_in_tiles_after_place_next() {
let engine = Engine::new();
let coordinate = Coordinate::from([0, 1]);
fn test_tile_exists_after_place_next() {
let tile = Tile::new();
let engine = Engine::new_with_tile_pool(vec![tile]);
let coordinate = Coordinate::from([0, 0]);
let new_engine = engine
.place_next(coordinate.clone(), Orientation::Up)
.unwrap();

assert!(!engine.get_tiles().contains_key(&coordinate));
assert!(new_engine.get_tiles().contains_key(&coordinate));
assert!(!engine.get_board().contains_key(&coordinate));
assert!(new_engine.get_board().contains_key(&coordinate));
}

#[test]
fn test_tile_exists_in_tiles_after_place() {
let engine = Engine::new();
let coordinate = Coordinate::from([0, 1]);
fn test_tile_exists_after_place() {
let tile = Tile::new();
let engine = Engine::new_with_tile_pool(vec![tile.clone()]);
let coordinate = Coordinate::from([0, 0]);
let orientation = Orientation::Up;

let new_engine = engine
.place(tile.clone(), coordinate.clone(), orientation.clone())
.unwrap();

let original_contains_tile = engine.get_tiles().contains_key(&coordinate);
let new_contained_tile_placement = new_engine.get_tiles().get(&coordinate).unwrap();
let original_contains_tile = engine.get_board().contains_key(&coordinate);
let new_contained_tile_placement = new_engine.get_board().get(&coordinate).unwrap();

assert!(!original_contains_tile);
assert_eq!(new_contained_tile_placement, &(tile, orientation));
}

#[test]
fn test_cannot_place_on_center() {
let engine = Engine::new();
let result = engine.place_next([0, 0], Orientation::Up);
fn test_can_get_next_tile() {
let tile = Tile::new();
let engine = Engine::new_with_tile_pool(vec![tile]);

let next_tile = engine.get_next_tile();

assert_eq!(result,
Err(vec![PlacementError::TileAlreadyAtCoordinate, PlacementError::NotAdjacent]));
assert!(next_tile.is_some());
}
}
}
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ mod models;
mod engine;
mod placement_error;
mod rules;
mod tile_factory;
mod standard_tile_factory;

use models::direction;

Expand Down
2 changes: 0 additions & 2 deletions src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,3 @@ pub mod feature;
pub mod tile;
pub mod coordinate;
pub mod orientation;

use ::*;
1 change: 1 addition & 0 deletions src/models/orientation.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#[derive(PartialEq, Eq, Debug, Clone)]
#[allow(dead_code)]
pub enum Orientation {
Up,
Right,
Expand Down
4 changes: 3 additions & 1 deletion src/placement_error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#[derive(Debug, PartialEq)]
#[allow(dead_code)]
pub enum PlacementError {
NotAdjacent,
TileAlreadyAtCoordinate,
}
PoolOutOfTiles,
}
10 changes: 9 additions & 1 deletion src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ use models::tile::Tile;
use placement_error::PlacementError;
use engine::TileMap;

pub fn check(board: &TileMap, tile: &Tile, coordinate: &Coordinate, orientation: &Orientation) -> Result<(), Vec<PlacementError>> {
pub fn check(board: &TileMap,
tile: &Tile,
coordinate: &Coordinate,
orientation: &Orientation)
-> Result<(), Vec<PlacementError>> {

let checks: Vec<fn(&TileMap, &Coordinate) -> Option<PlacementError>> =
vec![check_tile_already_at_coordinate, check_not_adjecent];
Expand Down Expand Up @@ -34,6 +38,10 @@ fn check_tile_already_at_coordinate(board: &TileMap,
}

fn check_not_adjecent(board: &TileMap, coordinate: &Coordinate) -> Option<PlacementError> {
if board.len() == 0 {
return None;
}

//////// UNDERENGINEERED
//////// TODO: OVERENGINEER
let has_adjecent_tiles = board.contains_key(&[coordinate.x, coordinate.y - 1].into()) ||
Expand Down
10 changes: 10 additions & 0 deletions src/standard_tile_factory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use tile_factory::TileFactory;
use models::tile::Tile;

pub struct StandardTileFactory;

impl TileFactory for StandardTileFactory {
fn get_tile_pool() -> Vec<Tile> {
Vec::new()
}
}
5 changes: 5 additions & 0 deletions src/tile_factory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use models::tile::Tile;

pub trait TileFactory {
fn get_tile_pool() -> Vec<Tile>;
}