Skip to content

Commit

Permalink
Giving tiles its own two-axis iterator implementation based on the Gr…
Browse files Browse the repository at this point in the history
…id struct in the maze example
  • Loading branch information
sunjay committed Dec 17, 2017
1 parent 99c03a9 commit 5bbbc0c
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 12 deletions.
11 changes: 4 additions & 7 deletions examples/reversi/board.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ use std::ops::Deref;

use turtle::Color;

/// (Row, Column)
pub type Position = (usize, usize);

type Tiles = [[Option<Piece>; 8]; 8];
use tiles::{Tiles, Position};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Piece {
Expand Down Expand Up @@ -36,11 +33,11 @@ impl Piece {
}
}

fn valid_moves_for(tiles: Tiles, piece: Piece) -> Vec<Position> {
fn valid_moves_for(tiles: &Tiles, piece: Piece) -> Vec<Position> {
Default::default() //TODO
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug)]
pub struct Board {
current: Piece,
/// None - empty tile
Expand Down Expand Up @@ -68,7 +65,7 @@ impl Board {
tiles[4][3] = Some(Piece::B);
tiles[4][4] = Some(Piece::A);
let current = Piece::A;
let valid_moves = valid_moves_for(tiles, current);
let valid_moves = valid_moves_for(&tiles, current);

Self {
current,
Expand Down
11 changes: 6 additions & 5 deletions examples/reversi/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
extern crate turtle;

mod board;
mod tiles;

use std::f64::consts::PI;

Expand Down Expand Up @@ -33,8 +34,8 @@ fn main() {
let width = 580.0;
let height = 580.0;
let board = Board::new();
let rows = board.len();
let cols = board[0].len();
let rows = board.col_size();
let cols = board.row_size();

// These values are used quite often, so it makes sense to compute them in advance so that
// we don't need to keep repeating ourselves
Expand All @@ -47,6 +48,7 @@ fn main() {
tile_height: height / rows as f64,
};

turtle.set_speed("instant"); //TODO: Remove this line
turtle.pen_up();
turtle.forward(height / 2.0);
turtle.right(90.0);
Expand Down Expand Up @@ -87,8 +89,8 @@ fn draw_board(turtle: &mut Turtle, dim: &Dimensions) {

fn draw_board_pieces(turtle: &mut Turtle, board: &Board, dim: &Dimensions) {
// Draw starting pieces
for (row, row_pieces) in board.iter().enumerate() {
for (col, piece) in row_pieces.iter().enumerate() {
for (row, row_pieces) in board.rows().enumerate() {
for (col, piece) in row_pieces.enumerate() {
if let &Some(piece) = piece {
move_to_tile(turtle, (row, col), &dim);
draw_piece(turtle, piece, &dim);
Expand Down Expand Up @@ -117,7 +119,6 @@ fn play_game(turtle: &mut Turtle, mut board: Board, dim: &Dimensions) {
let row = ((1.0 - (mouse[1] + dim.height/2.0) / dim.height) * dim.rows as f64).floor() as isize;
let col = ((mouse[0] + dim.width/2.0) / dim.width * dim.cols as f64).floor() as isize;

println!("Play {:?}", (row, col));
if row >= 0 && row < dim.rows as isize
&& col >= 0 && col < dim.cols as isize
&& board.is_valid_move(&(row as usize, col as usize)) {
Expand Down
229 changes: 229 additions & 0 deletions examples/reversi/tiles.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
use std::ops::{Index, IndexMut};

use board::Piece;

const SIZE: usize = 8;

/// (Row, Column)
pub type Position = (usize, usize);

/// A row or column of pieces
pub type Pieces = [Option<Piece>; SIZE];

/// Used to determine whether to iterate over rows or columns
#[derive(Clone, Copy)]
enum TilesIterTarget {
Rows,
Columns,
DiagonalsTLBR,
DiagonalsTRBL,
}

pub struct TilesIter<'a> {
grid: &'a [Pieces; SIZE],
target: TilesIterTarget,
current: usize,
end: usize,
}

impl<'a> TilesIter<'a> {
fn new(grid: &'a [Pieces; SIZE], target: TilesIterTarget) -> Self {
Self {
grid,
target,
current: 0,
end: match target {
TilesIterTarget::Rows => grid.len(),
TilesIterTarget::Columns => grid[0].len(),
TilesIterTarget::DiagonalsTLBR => unimplemented!(),
TilesIterTarget::DiagonalsTRBL => unimplemented!(),
},
}
}
}

impl<'a> Iterator for TilesIter<'a> {
type Item = TilesPieceIter<'a>;

fn next(&mut self) -> Option<Self::Item> {
if self.current >= self.end {
return None;
}

let iter = TilesPieceIter::new(
self.grid,
self.target,
self.current
);

self.current += 1;
Some(iter)
}
}

impl<'a> DoubleEndedIterator for TilesIter<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.current >= self.end {
return None;
}

let iter = TilesPieceIter::new(
self.grid,
self.target,
self.end - 1
);

self.end -= 1;
Some(iter)
}
}

pub struct TilesPieceIter<'a> {
grid: &'a [Pieces; SIZE],
target: TilesIterTarget,
/// The row or column being iterated over
index: usize,
/// The current position in the row or column being iterated over
current: usize,
// Index of the item just after the last item in the row or column being iterated over
end: usize,
}

impl<'a> TilesPieceIter<'a> {
fn new(grid: &'a [Pieces; SIZE], target: TilesIterTarget, index: usize) -> Self {
Self {
grid,
target,
index,
current: 0,
end: match target {
TilesIterTarget::Rows => grid[0].len(),
TilesIterTarget::Columns => grid.len(),
TilesIterTarget::DiagonalsTLBR => unimplemented!(),
TilesIterTarget::DiagonalsTRBL => unimplemented!(),
},
}
}

fn get(&self, current: usize) -> &'a Option<Piece> {
match self.target {
TilesIterTarget::Rows => {
&self.grid[self.index][current]
},
TilesIterTarget::Columns => {
&self.grid[current][self.index]
},
TilesIterTarget::DiagonalsTLBR => unimplemented!(),
TilesIterTarget::DiagonalsTRBL => unimplemented!(),
}
}
}

impl<'a> Iterator for TilesPieceIter<'a> {
type Item = &'a Option<Piece>;

fn next(&mut self) -> Option<Self::Item> {
if self.current >= self.end {
return None;
}

let cell = self.get(self.current);

self.current += 1;
Some(cell)
}
}

impl<'a> DoubleEndedIterator for TilesPieceIter<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.current >= self.end {
return None;
}

let cell = self.get(self.end - 1);

self.end -= 1;
Some(cell)
}
}

#[derive(Debug, Default)]
pub struct Tiles([Pieces; SIZE]);

impl Index<usize> for Tiles {
type Output = Pieces;

fn index(&self, index: usize) -> &Self::Output {
self.0.index(index)
}
}

impl IndexMut<usize> for Tiles {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
self.0.index_mut(index)
}
}

impl Tiles {
/// Returns the positions of adjacent pieces
pub fn adjacent_pieces(&self, (row, col): Position) -> Vec<Position> {
let row = row as isize;
let col = col as isize;
let test: [(isize, isize); 8] = [
(row-1, col-1),
(row-1, col),
(row-1, col+1),
(row, col-1),
(row, col+1),
(row+1, col-1),
(row+1, col),
(row+1, col+1),
];

test.into_iter().filter_map(|&pos| {
self.get(pos).map(|_| (pos.0 as usize, pos.1 as usize))
}).collect()
}

/// Gets the piece at the given position, if there is any
pub fn get(&self, (row, col): (isize, isize)) -> Option<Piece> {
if row > 0 && col > 0 {
*self.0.get(row as usize)
.map(|r| r.get(col as usize).unwrap_or(&None))
.unwrap_or(&None)
}
else {
None
}
}

/// Returns the size of each row
pub fn row_size(&self) -> usize {
self.0[0].len()
}

/// Returns the size of each column
pub fn col_size(&self) -> usize {
self.0.len()
}

/// Returns an iterator over each row
pub fn rows(&self) -> TilesIter {
TilesIter::new(&self.0, TilesIterTarget::Rows)
}

/// Returns an iterator over each row
pub fn cols(&self) -> TilesIter {
TilesIter::new(&self.0, TilesIterTarget::Columns)
}

/// Returns an iterator over each top-left to bottom-right diagonal
pub fn diagonals_tlbr(&self) -> TilesIter {
TilesIter::new(&self.0, TilesIterTarget::DiagonalsTLBR)
}

/// Returns an iterator over each top-right to bottom-left diagonal
pub fn diagonals_trbl(&self) -> TilesIter {
TilesIter::new(&self.0, TilesIterTarget::DiagonalsTRBL)
}
}

0 comments on commit 5bbbc0c

Please sign in to comment.