use crate::board::Board; use crate::lexer::{lexer, DecimalToken, Token}; use crate::parser; use crate::parser::{Expression, Expressions}; use crate::position::Position2d; use crate::tile::{Operator, Tile}; /// Evaluates a single expression syntax tree. pub fn calculate(expr: &Expression) -> f64 { match expr { Expression::Digit(value) => *value as f64, Expression::Parentheses(expr) => calculate(expr), Expression::Unary(operator, expr) => { let value = calculate(expr); match operator { Operator::Subtract => -value, _ => panic!("Invalid unary operator"), } } Expression::Binary(operator, left, right) => { let left = calculate(left); let right = calculate(right); match operator { Operator::Add => left + right, Operator::Subtract => left - right, Operator::Multiply => left * right, Operator::Divide => left / right, } } } } /// Evaluates a vector of expression syntax trees. pub fn are_valid_expressions(expr: &Expressions) -> bool { let mut res: Option = None; for expr in expr { let value = calculate(expr); if let Some(res) = res { if res != value { return false; } } else { res = Some(value); } } res.is_some() } /// Determines if the given tokens are a valid equation. pub fn is_valid_guess_of_tokens(tokens: &[Token]) -> bool { let mut res: Option = None; for part in tokens.split(|token| matches!(token, Token::Equals)) { let rpn = shunting_yard(part); if let Ok(rpn) = rpn { let value = evaluate_rpn(&rpn); if let Ok(value) = value { if let Some(res) = res { if res != value { return false; } } else { res = Some(value); } } else { return false; } } else { return false; } } res.is_some() } pub fn is_valid_guess(board: &Board, positions: &[Position2d]) -> Result { let tiles = positions .iter() .map(|&pos| board.get(pos.x, pos.y)) .collect::>>() .ok_or(())?; let tokens = lexer(&tiles); let expressions = parser::parse(&tokens)?; if expressions.len() < 2 { Ok(false) } else { Ok(are_valid_expressions(&expressions)) } } /// Convert an infix expression to a postfix expression. fn shunting_yard(tokens: &[Token]) -> Result, ()> { let mut operator_stack: Vec = Vec::with_capacity(tokens.len()); let mut output: Vec = Vec::with_capacity(tokens.len()); for token in tokens { match token { Token::NumberLiteral(num) => output.push(DecimalToken::NumberLiteral(*num as f64)), Token::Operator(op) => { while let Some(DecimalToken::Operator(top_op)) = operator_stack.last() { if top_op.precedence() >= op.precedence() { output.push(operator_stack.pop().unwrap()); } else { break; } } operator_stack.push(DecimalToken::Operator(*op)); } Token::LeftParen => operator_stack.push(DecimalToken::LeftParen), Token::RightParen => { while let Some(top_op) = operator_stack.pop() { if let DecimalToken::LeftParen = top_op { break; } else { output.push(top_op); } } } _ => panic!("Unexpected token: {token:?}"), } } while let Some(top_op) = operator_stack.pop() { output.push(top_op); } Ok(output) } /// Evaluate a postfix expression. fn evaluate_rpn(tokens: &[DecimalToken]) -> Result { let mut stack = Vec::new(); for token in tokens { match token { DecimalToken::NumberLiteral(num) => stack.push(*num), DecimalToken::Operator(op) => { let right = stack.pop().ok_or(())?; let left = stack.pop().ok_or(())?; let result = match op { Operator::Add => left + right, Operator::Subtract => left - right, Operator::Multiply => left * right, Operator::Divide => left / right, }; stack.push(result); } _ => panic!("Unexpected token: {token:?}"), } } stack.pop().ok_or(()) } #[cfg(test)] mod tests { use super::*; #[test] fn test_calculate() { let expr = Expression::Binary( Operator::Add, Box::new(Expression::Digit(1)), Box::new(Expression::Digit(2)), ); assert_eq!(calculate(&expr), 3.0); } #[test] fn test_are_valid_expressions() { let expr = vec![ Expression::Binary( Operator::Add, Box::new(Expression::Digit(3)), Box::new(Expression::Digit(4)), ), Expression::Binary( Operator::Add, Box::new(Expression::Digit(6)), Box::new(Expression::Digit(1)), ), ]; assert!(are_valid_expressions(&expr)); let expr = vec![ Expression::Digit(9), Expression::Binary( Operator::Add, Box::new(Expression::Digit(7)), Box::new(Expression::Digit(1)), ), ]; assert!(!are_valid_expressions(&expr)); } #[test] fn test_are_valid_expressions_negative() { let expr = vec![ Expression::Binary( Operator::Add, Box::new(Expression::Digit(2)), Box::new(Expression::Digit(-5)), ), Expression::Binary( Operator::Subtract, Box::new(Expression::Digit(-4)), Box::new(Expression::Digit(-1)), ), ]; assert!(are_valid_expressions(&expr)); } #[test] fn test_are_valid_expressions_unary_negative() { let expr = vec![ Expression::Unary(Operator::Subtract, Box::new(Expression::Digit(8))), Expression::Digit(-8), ]; assert!(are_valid_expressions(&expr)); } #[test] fn shunting_yard_sample() { let tokens = vec![ Token::NumberLiteral(5), Token::Operator(Operator::Multiply), Token::NumberLiteral(9), Token::Operator(Operator::Subtract), Token::NumberLiteral(2), ]; let res = shunting_yard(&tokens).expect("Failed to evaluate"); assert_eq!( res, vec![ DecimalToken::NumberLiteral(5.), DecimalToken::NumberLiteral(9.), DecimalToken::Operator(Operator::Multiply), DecimalToken::NumberLiteral(2.), DecimalToken::Operator(Operator::Subtract), ] ); } #[test] fn shunting_yard_precedence() { let tokens = vec![ Token::NumberLiteral(1), Token::Operator(Operator::Add), Token::NumberLiteral(2), Token::Operator(Operator::Multiply), Token::NumberLiteral(3), Token::Operator(Operator::Add), Token::NumberLiteral(4), ]; let res = shunting_yard(&tokens).expect("Failed to evaluate"); assert_eq!( res, vec![ DecimalToken::NumberLiteral(1.), DecimalToken::NumberLiteral(2.), DecimalToken::NumberLiteral(3.), DecimalToken::Operator(Operator::Multiply), DecimalToken::Operator(Operator::Add), DecimalToken::NumberLiteral(4.), DecimalToken::Operator(Operator::Add), ] ); } #[test] fn is_valid_guess_of_tiles_equals() { let tokens = vec![ Token::NumberLiteral(4), Token::Operator(Operator::Subtract), Token::NumberLiteral(1), Token::Equals, Token::NumberLiteral(3), ]; assert!(is_valid_guess_of_tokens(&tokens)); } #[test] fn is_valid_guess_of_tiles_not_equals() { let tokens = vec![ Token::NumberLiteral(8), Token::Operator(Operator::Divide), Token::NumberLiteral(4), Token::Equals, Token::NumberLiteral(5), ]; assert!(!is_valid_guess_of_tokens(&tokens)); } #[test] fn is_valid_guess_of_tiles_precedence() { let tokens = vec![ Token::NumberLiteral(5), Token::Operator(Operator::Add), Token::NumberLiteral(-2), Token::Operator(Operator::Multiply), Token::NumberLiteral(3), Token::Equals, Token::NumberLiteral(-1), ]; assert!(is_valid_guess_of_tokens(&tokens)); } }