Include a serde feature in the shared crate
This commit is contained in:
@@ -7,8 +7,10 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
enum-map = "2.4.2"
|
||||
itertools = { version = "0.10.5", optional = true }
|
||||
itertools = { version = "0.12.1", optional = true }
|
||||
rand = "0.8.5"
|
||||
serde = { version = "1.0.197", features = ["derive"], optional = true }
|
||||
|
||||
[features]
|
||||
ai = ["itertools"]
|
||||
ai = ["dep:itertools"]
|
||||
serde = ["dep:serde"]
|
||||
|
@@ -12,6 +12,7 @@ const DEFAULT_BOARD_SIZE: usize = 19;
|
||||
/// to create a new board with a default size. To create a board with a custom
|
||||
/// size, use [`Board::new()`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Board {
|
||||
tiles: Vec<Option<Tile>>,
|
||||
width: usize,
|
||||
@@ -192,7 +193,7 @@ impl Board {
|
||||
}
|
||||
|
||||
/// Tests whether the given positions are part of the same chain.
|
||||
fn belong_to_same_chain(&self, positions: &Vec<Position2d>, direction: Direction) -> bool {
|
||||
fn belong_to_same_chain(&self, positions: &[Position2d], direction: Direction) -> bool {
|
||||
let mut it = positions.iter().copied().peekable();
|
||||
while let Some(mut pos) = it.next() {
|
||||
if let Some(&next) = it.peek() {
|
||||
|
@@ -54,11 +54,7 @@ impl Hand {
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, idx: usize) -> Option<Tile> {
|
||||
if idx < self.tiles.len() {
|
||||
Some(self.tiles.remove(idx))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
(idx < self.tiles.len()).then(|| self.tiles.remove(idx))
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Tile> {
|
||||
|
@@ -83,7 +83,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_lex() {
|
||||
let input = vec![
|
||||
let input = [
|
||||
Tile::Digit(Digit::new(1)),
|
||||
Tile::Digit(Digit::new(2)),
|
||||
Tile::Digit(Digit::new(3)),
|
||||
@@ -104,7 +104,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_lex_parentheses() {
|
||||
let input = vec![
|
||||
let input = [
|
||||
Tile::Digit(Digit::new(1)),
|
||||
Tile::Operator(Operator::Subtract),
|
||||
Tile::Digit(Digit {
|
||||
@@ -133,7 +133,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_lex_parentheses_long() {
|
||||
let input = vec![
|
||||
let input = [
|
||||
Tile::Digit(Digit {
|
||||
value: 1,
|
||||
has_left_parenthesis: true,
|
||||
|
@@ -1,15 +1,15 @@
|
||||
///! # Scrabble with Numbers
|
||||
///!
|
||||
///! This crate provides the core game logic for the Scrabble with Numbers game.
|
||||
///! It can be used standalone, or as a library for other projects.
|
||||
///!
|
||||
///! ## Features
|
||||
///! - Create and verify game expressions, such as `2*3+4*5`.
|
||||
///! - Generate valid automatic moves for a given board state, with the `ai` feature.
|
||||
///! - Check if a player move valid.
|
||||
///! - Calculate the score of an expression.
|
||||
///!
|
||||
///! If you are looking for a server implementation, see the `board-server` crate.
|
||||
//! # Scrabble with Numbers
|
||||
//!
|
||||
//! This crate provides the core game logic for the Scrabble with Numbers game.
|
||||
//! It can be used standalone, or as a library for other projects.
|
||||
//!
|
||||
//! ## Features
|
||||
//! - Create and verify game expressions, such as `2*3+4*5`.
|
||||
//! - Generate valid automatic moves for a given board state, with the `ai` feature.
|
||||
//! - Check if a player move valid.
|
||||
//! - Calculate the score of an expression.
|
||||
//!
|
||||
//! If you are looking for a server implementation, see the `board-server` crate.
|
||||
|
||||
#[cfg(feature = "ai")]
|
||||
pub mod ai;
|
||||
|
@@ -39,20 +39,17 @@ pub fn parse(tokens: &[Token]) -> Result<Expressions, ()> {
|
||||
fn parse_primary<'a>(
|
||||
tokens: &mut Peekable<impl Iterator<Item = &'a Token>>,
|
||||
) -> Result<Expression, ()> {
|
||||
if let Some(Token::NumberLiteral(value)) = tokens.peek() {
|
||||
tokens.next();
|
||||
Ok(Expression::Digit(*value))
|
||||
} else if let Some(Token::LeftParen) = tokens.peek() {
|
||||
tokens.next();
|
||||
let expr = parse_term(tokens)?;
|
||||
if let Some(Token::RightParen) = tokens.peek() {
|
||||
tokens.next();
|
||||
Ok(Expression::Parentheses(Box::new(expr)))
|
||||
} else {
|
||||
Err(())
|
||||
match tokens.next() {
|
||||
Some(Token::NumberLiteral(value)) => Ok(Expression::Digit(*value)),
|
||||
Some(Token::LeftParen) => {
|
||||
let expr = parse_term(tokens)?;
|
||||
if let Some(Token::RightParen) = tokens.next() {
|
||||
Ok(Expression::Parentheses(Box::new(expr)))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(())
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +101,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
let tokens = vec![
|
||||
let tokens = [
|
||||
Token::NumberLiteral(1),
|
||||
Token::Operator(Operator::Add),
|
||||
Token::NumberLiteral(2),
|
||||
@@ -128,7 +125,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_reverse() {
|
||||
let tokens = vec![
|
||||
let tokens = [
|
||||
Token::NumberLiteral(2),
|
||||
Token::Operator(Operator::Multiply),
|
||||
Token::NumberLiteral(3),
|
||||
@@ -152,7 +149,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_parentheses() {
|
||||
let tokens = vec![
|
||||
let tokens = [
|
||||
Token::LeftParen,
|
||||
Token::NumberLiteral(1),
|
||||
Token::Operator(Operator::Add),
|
||||
@@ -178,7 +175,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_equals() {
|
||||
let tokens = vec![
|
||||
let tokens = [
|
||||
Token::NumberLiteral(1),
|
||||
Token::Equals,
|
||||
Token::NumberLiteral(2),
|
||||
@@ -198,7 +195,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_unary_and_binary_minus() {
|
||||
let tokens = vec![
|
||||
let tokens = [
|
||||
Token::Operator(Operator::Subtract),
|
||||
Token::NumberLiteral(1),
|
||||
Token::Operator(Operator::Subtract),
|
||||
@@ -220,7 +217,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_unary_before_parenthesis() {
|
||||
let tokens = vec![
|
||||
let tokens = [
|
||||
Token::Operator(Operator::Subtract),
|
||||
Token::LeftParen,
|
||||
Token::NumberLiteral(9),
|
||||
@@ -244,7 +241,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_double_unary() {
|
||||
let tokens = vec![
|
||||
let tokens = [
|
||||
Token::Operator(Operator::Subtract),
|
||||
Token::Operator(Operator::Subtract),
|
||||
Token::NumberLiteral(7),
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/// A position in a 2d grid.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Position2d {
|
||||
pub x: usize,
|
||||
pub y: usize,
|
||||
|
@@ -14,14 +14,10 @@ fn calc_expression_score(tiles: &[Tile]) -> u32 {
|
||||
for token in tiles {
|
||||
match token {
|
||||
Tile::Digit(_) => digit_score += 1,
|
||||
Tile::Operator(op) => {
|
||||
match op {
|
||||
Operator::Add | Operator::Subtract => multiplier = 2,
|
||||
Operator::Multiply => multiplier = 3,
|
||||
Operator::Divide => digit_score += 10,
|
||||
};
|
||||
}
|
||||
_ => unreachable!(),
|
||||
Tile::Operator(Operator::Add | Operator::Subtract) => multiplier = 2,
|
||||
Tile::Operator(Operator::Multiply) => multiplier = 3,
|
||||
Tile::Operator(Operator::Divide) => digit_score += 10,
|
||||
Tile::Equals => unreachable!(),
|
||||
}
|
||||
}
|
||||
digit_score * multiplier
|
||||
|
@@ -1,8 +1,10 @@
|
||||
use enum_map::Enum;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// A single digit that can be wrapped in parentheses.
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Digit {
|
||||
pub value: i8,
|
||||
pub has_left_parenthesis: bool,
|
||||
@@ -61,6 +63,7 @@ impl TryFrom<&str> for Digit {
|
||||
|
||||
/// An operator that can be applied between two terms.
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Enum)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Operator {
|
||||
Add,
|
||||
Subtract,
|
||||
@@ -104,6 +107,7 @@ impl TryFrom<char> for Operator {
|
||||
|
||||
/// A single piece of a mathematical expression.
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Tile {
|
||||
Digit(Digit),
|
||||
Operator(Operator),
|
||||
@@ -114,6 +118,14 @@ impl TryFrom<&str> for Tile {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
Self::from_str(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Tile {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||
if let Ok(digit) = Digit::try_from(value) {
|
||||
return Ok(Tile::Digit(digit));
|
||||
}
|
||||
|
Reference in New Issue
Block a user