Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ptdecker committed Apr 12, 2024
1 parent ec0be7b commit 4400663
Show file tree
Hide file tree
Showing 10 changed files with 417 additions and 13 deletions.
13 changes: 2 additions & 11 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,15 @@
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb


# Added by cargo

/target

# IDE

# Added by cargo
#
# already existing elements were commented out

#/target
/.idea
131 changes: 131 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "lib-tic-tac-toe"
version = "0.1.0"
edition = "2021"
authors = ["P. Todd Decker <[email protected]>"]

[dependencies]
rand = "0.9.0-alpha.1"
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
# lib-tic-tac-toe
Rust-based Tic-Tac-Toe Library
# Rust-based Tic-Tac-Toe Library (lib-tic-tac-toe)

This is a Rust-based Tic-Tac-Toe library meant to be incorporated into various
Tic-Tac-Toe game implementations.

## no_std Compatibility

This library is designed to be compatible with no_std environments. It does not
require the Rust standard library (std) and depends only on the core allocation
(alloc) library for memory management tasks. This makes it suitable for use in
bare-metal, embedded, or other high-performance or resource-constrained
environments.
84 changes: 84 additions & 0 deletions src/board.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//! Tic-Tac-Toe Game Board

use super::*;

use crate::mark::Mark;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(transparent)]
pub(crate) struct Space(u8);

impl TryFrom<u8> for Space {
type Error = Error;

fn try_from(value: u8) -> Result<Self, Self::Error> {
if value > 8 {
Err(Error::InvalidSpace)
} else {
Ok(Space(value))
}
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(crate) struct Board {
spaces: [Mark; 9],
}

impl Board {
pub(crate) fn new() -> Self {
Board {
spaces: [Mark::Blank; 9],
}
}

fn random() -> Self {
let mut rng = rand::thread_rng();
let spaces: [Mark; 9] = rng.gen();
Board { spaces }
}

pub(crate) fn mark(&mut self, space: Space, mark: Mark) -> Result<(), Error> {
self.spaces[space.0 as usize] = mark;
Ok(())
}

fn get(&self, space: Space) -> Result<Mark, Error> {
Ok(self.spaces[space.0 as usize])
}
}

impl fmt::Display for Board {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let lines = [(0, 1, 2), (3, 4, 5), (6, 7, 8)];

for (i, line) in lines.iter().enumerate() {
if i > 0 {
writeln!(f, "---+---+---")?;
}
writeln!(
f,
" {} | {} | {} ",
self.spaces[line.0], self.spaces[line.1], self.spaces[line.2]
)?;
}
Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn blank_board_display() {
let board_display = alloc::format!("{}", Board::new());
let expected_display = " | | \n---+---+---\n | | \n---+---+---\n | | \n";
assert_eq!(board_display, expected_display);
}

#[test]
fn random_boards_not_equal() {
assert_ne!(Board::random(), Board::random());
}
}
20 changes: 20 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//! Tic-Tac-Toe Errors

use super::*;

#[derive(Debug, Clone)]
pub enum Error {
InvalidSpace,
InvalidMove,
HistoryFull,
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::InvalidSpace => write!(f, "invalid space"),
Error::InvalidMove => write!(f, "invalid move"),
Error::HistoryFull => write!(f, "game history full"),
}
}
}
33 changes: 33 additions & 0 deletions src/game.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//! Tic-Tac-Toe Game

use super::*;

use crate::{
board::Board,
history::History,
mark::Mark,
};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
struct Game {
turn: Mark,
board: Board,
history: History,
}

impl Game {
pub fn new() -> Self {
Self {
turn: Mark::X,
board: Board::new(),
history: History::new(),
}
}
pub fn mark(&mut self, space: u8) -> Result<(), Error> {
let space = space.try_into()?;
self.board.mark(space, self.turn)?;
self.history.add(space)?;
self.turn.next();
Ok(())
}
}
28 changes: 28 additions & 0 deletions src/history.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//! Tic-Tac-Toe History

use super::*;

use crate::board::Space;

const MAX_HISTORY_SIZE: usize = 9;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(transparent)]
pub(crate) struct History((u8, [Option<Space>; MAX_HISTORY_SIZE]));

impl History {
pub(crate) fn new() -> Self {
History((0u8, [None; MAX_HISTORY_SIZE]))
}
fn len(&self) -> usize {
self.0 .0 as usize
}
pub(crate) fn add(&mut self, space: Space) -> Result<(), Error> {
if self.0 .0 as usize == MAX_HISTORY_SIZE {
return Err(Error::HistoryFull);
};
self.0 .0 += 1;
self.0 .1[self.0 .0 as usize] = Some(space);
Ok(())
}
}
19 changes: 19 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//! Tic-Tac-Toe Library
#![no_std]
#![allow(dead_code)]

extern crate alloc;

mod board;
mod error;
mod game;
mod history;
mod mark;

use core::fmt;
use rand::{
distributions::{Distribution, Standard},
Rng,
};

pub use error::Error;
Loading

0 comments on commit 4400663

Please sign in to comment.