Reworked parsing
parent
a0ffc73333
commit
a2a264163a
329
src/board.rs
329
src/board.rs
|
@ -1,26 +1,167 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::{Debug, Display};
|
||||||
use termion::color;
|
use termion::color;
|
||||||
|
|
||||||
use crate::{Player, Symb};
|
use crate::{Player, Symb};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
enum Token {
|
pub enum Token {
|
||||||
Number(f32),
|
Value(String),
|
||||||
OpAdd,
|
OpAdd,
|
||||||
OpSub,
|
OpSub,
|
||||||
OpMult,
|
OpMult,
|
||||||
OpDiv,
|
OpDiv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Token {
|
#[derive(PartialEq, Clone)]
|
||||||
fn val(&self) -> f32 {
|
pub enum TreeElement {
|
||||||
|
Partial(String),
|
||||||
|
Number(f32),
|
||||||
|
Add {
|
||||||
|
l: Box<TreeElement>,
|
||||||
|
r: Box<TreeElement>,
|
||||||
|
},
|
||||||
|
Sub {
|
||||||
|
l: Box<TreeElement>,
|
||||||
|
r: Box<TreeElement>,
|
||||||
|
},
|
||||||
|
Mul {
|
||||||
|
l: Box<TreeElement>,
|
||||||
|
r: Box<TreeElement>,
|
||||||
|
},
|
||||||
|
Div {
|
||||||
|
l: Box<TreeElement>,
|
||||||
|
r: Box<TreeElement>,
|
||||||
|
},
|
||||||
|
Neg {
|
||||||
|
r: Box<TreeElement>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TreeElement {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Number(x) => *x,
|
Self::Partial(s) => write!(f, "{s}")?,
|
||||||
_ => unreachable!(),
|
Self::Number(n) => write!(f, "{n}")?,
|
||||||
|
Self::Add { l, r } => write!(f, "({l}+{r})")?,
|
||||||
|
Self::Div { l, r } => write!(f, "({l}÷{r})")?,
|
||||||
|
Self::Mul { l, r } => write!(f, "({l}×{r})")?,
|
||||||
|
Self::Sub { l, r } => write!(f, "({l}-{r})")?,
|
||||||
|
Self::Neg { r } => write!(f, "(-{r})")?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for TreeElement {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Display::fmt(&self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum InterTreeElement {
|
||||||
|
Unprocessed(Token),
|
||||||
|
Processed(TreeElement),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl TreeElement {
|
||||||
|
pub fn left(&self) -> Option<&TreeElement> {
|
||||||
|
match self {
|
||||||
|
Self::Add { l, .. }
|
||||||
|
| Self::Sub { l, .. }
|
||||||
|
| Self::Mul { l, .. }
|
||||||
|
| Self::Div { l, .. } => Some(&**l),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn right(&self) -> Option<&TreeElement> {
|
||||||
|
match self {
|
||||||
|
Self::Add { r, .. }
|
||||||
|
| Self::Neg { r, .. }
|
||||||
|
| Self::Sub { r, .. }
|
||||||
|
| Self::Mul { r, .. }
|
||||||
|
| Self::Div { r, .. } => Some(&**r),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn left_mut(&mut self) -> Option<&mut TreeElement> {
|
||||||
|
match self {
|
||||||
|
Self::Add { l, .. }
|
||||||
|
| Self::Sub { l, .. }
|
||||||
|
| Self::Mul { l, .. }
|
||||||
|
| Self::Div { l, .. } => Some(&mut **l),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn right_mut(&mut self) -> Option<&mut TreeElement> {
|
||||||
|
match self {
|
||||||
|
Self::Add { r, .. }
|
||||||
|
| Self::Neg { r, .. }
|
||||||
|
| Self::Sub { r, .. }
|
||||||
|
| Self::Mul { r, .. }
|
||||||
|
| Self::Div { r, .. } => Some(&mut **r),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TreeElement {
|
||||||
|
pub fn evaluate(&self) -> Option<f32> {
|
||||||
|
match self {
|
||||||
|
Self::Number(x) => Some(*x),
|
||||||
|
Self::Partial(_) => None,
|
||||||
|
Self::Add { l, r } => {
|
||||||
|
let l = l.evaluate();
|
||||||
|
let r = r.evaluate();
|
||||||
|
if let (Some(l), Some(r)) = (l, r) {
|
||||||
|
Some(l + r)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Mul { l, r } => {
|
||||||
|
let l = l.evaluate();
|
||||||
|
let r = r.evaluate();
|
||||||
|
if let (Some(l), Some(r)) = (l, r) {
|
||||||
|
Some(l * r)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Div { l, r } => {
|
||||||
|
let l = l.evaluate();
|
||||||
|
let r = r.evaluate();
|
||||||
|
if let (Some(l), Some(r)) = (l, r) {
|
||||||
|
Some(l / r)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Sub { l, r } => {
|
||||||
|
let l = l.evaluate();
|
||||||
|
let r = r.evaluate();
|
||||||
|
if let (Some(l), Some(r)) = (l, r) {
|
||||||
|
Some(l - r)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Neg { r } => {
|
||||||
|
let r = r.evaluate();
|
||||||
|
if let Some(r) = r {
|
||||||
|
Some(-r)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Board {
|
pub struct Board {
|
||||||
board: [Option<(Symb, Player)>; 11],
|
board: [Option<(Symb, Player)>; 11],
|
||||||
free_spots: usize,
|
free_spots: usize,
|
||||||
|
@ -152,88 +293,159 @@ impl Board {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn evaluate(&self) -> Option<f32> {
|
pub fn tokenize(&self) -> Vec<Token> {
|
||||||
if !self.is_done() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut tokens = Vec::new();
|
let mut tokens = Vec::new();
|
||||||
let mut is_neg = true; // if true, - is negative. if false, subtract.
|
let mut is_neg = true; // if true, - is negative. if false, subtract.
|
||||||
let mut current_num = 0f32;
|
let mut current_num = String::new();
|
||||||
let mut current_sgn = 1f32;
|
|
||||||
|
|
||||||
for (s, _) in self.board.iter().map(|x| x.unwrap()) {
|
for s in self.board.iter().map(|x| x.map(|(s, _)| s)) {
|
||||||
match s {
|
match s {
|
||||||
Symb::Div => {
|
Some(Symb::Div) => {
|
||||||
tokens.push(Token::Number(current_num * current_sgn));
|
tokens.push(Token::Value(current_num.clone()));
|
||||||
current_num = 0.0;
|
current_num.clear();
|
||||||
current_sgn = 1.0;
|
|
||||||
tokens.push(Token::OpDiv);
|
tokens.push(Token::OpDiv);
|
||||||
is_neg = true;
|
is_neg = true;
|
||||||
}
|
}
|
||||||
Symb::Minus => {
|
Some(Symb::Minus) => {
|
||||||
if is_neg {
|
if is_neg {
|
||||||
current_sgn = -1.0;
|
current_num = format!("-{}", current_num);
|
||||||
} else {
|
} else {
|
||||||
tokens.push(Token::Number(current_num * current_sgn));
|
tokens.push(Token::Value(current_num.clone()));
|
||||||
current_num = 0.0;
|
current_num.clear();
|
||||||
current_sgn = 1.0;
|
|
||||||
tokens.push(Token::OpSub);
|
tokens.push(Token::OpSub);
|
||||||
is_neg = true;
|
is_neg = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Symb::Plus => {
|
Some(Symb::Plus) => {
|
||||||
tokens.push(Token::Number(current_num * current_sgn));
|
tokens.push(Token::Value(current_num.clone()));
|
||||||
current_num = 0.0;
|
current_num.clear();
|
||||||
current_sgn = 1.0;
|
|
||||||
tokens.push(Token::OpAdd);
|
tokens.push(Token::OpAdd);
|
||||||
is_neg = true;
|
is_neg = true;
|
||||||
}
|
}
|
||||||
Symb::Times => {
|
Some(Symb::Times) => {
|
||||||
tokens.push(Token::Number(current_num * current_sgn));
|
tokens.push(Token::Value(current_num.clone()));
|
||||||
current_num = 0.0;
|
current_num.clear();
|
||||||
current_sgn = 1.0;
|
|
||||||
tokens.push(Token::OpMult);
|
tokens.push(Token::OpMult);
|
||||||
is_neg = true;
|
is_neg = true;
|
||||||
}
|
}
|
||||||
Symb::Zero => {
|
Some(Symb::Zero) => {
|
||||||
current_num = current_num * 10.0;
|
current_num.push('0');
|
||||||
is_neg = false;
|
is_neg = false;
|
||||||
}
|
}
|
||||||
Symb::Number(x) => {
|
Some(Symb::Number(x)) => {
|
||||||
current_num = current_num * 10.0 + (x.get() as f32);
|
current_num.push_str(&x.to_string());
|
||||||
|
is_neg = false;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
current_num.push('_');
|
||||||
is_neg = false;
|
is_neg = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens.push(Token::Number(current_num * current_sgn));
|
tokens.push(Token::Value(current_num));
|
||||||
|
tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn treeify(tokens: &Vec<Token>) -> TreeElement {
|
||||||
|
let mut tree: Vec<_> = tokens
|
||||||
|
.iter()
|
||||||
|
.map(|x| InterTreeElement::Unprocessed(x.clone()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut priority_level = 0;
|
let mut priority_level = 0;
|
||||||
let mut did_something;
|
let mut did_something;
|
||||||
while tokens.len() > 1 {
|
while tree.len() > 1 {
|
||||||
did_something = false;
|
did_something = false;
|
||||||
for i in 0..tokens.len() {
|
for i in 0..tree.len() {
|
||||||
if match priority_level {
|
if match priority_level {
|
||||||
0 => matches!(tokens[i], Token::OpMult | Token::OpDiv),
|
0 => matches!(
|
||||||
1 => matches!(tokens[i], Token::OpAdd | Token::OpSub),
|
tree[i],
|
||||||
|
InterTreeElement::Unprocessed(Token::OpMult)
|
||||||
|
| InterTreeElement::Unprocessed(Token::OpDiv)
|
||||||
|
),
|
||||||
|
1 => matches!(
|
||||||
|
tree[i],
|
||||||
|
InterTreeElement::Unprocessed(Token::OpAdd)
|
||||||
|
| InterTreeElement::Unprocessed(Token::OpSub)
|
||||||
|
),
|
||||||
_ => false,
|
_ => false,
|
||||||
} {
|
} {
|
||||||
did_something = true;
|
did_something = true;
|
||||||
let l = &tokens[i - 1];
|
let l = &tree[i - 1];
|
||||||
let r = &tokens[i + 1];
|
let r = &tree[i + 1];
|
||||||
|
|
||||||
let v = match tokens[i] {
|
let l = match l {
|
||||||
Token::OpAdd => l.val() + r.val(),
|
InterTreeElement::Processed(x) => x.clone(),
|
||||||
Token::OpDiv => l.val() / r.val(),
|
InterTreeElement::Unprocessed(Token::Value(s)) => {
|
||||||
Token::OpSub => l.val() - r.val(),
|
if s.starts_with('-') {
|
||||||
Token::OpMult => l.val() * r.val(),
|
TreeElement::Neg {
|
||||||
|
r: {
|
||||||
|
if s.contains('_') {
|
||||||
|
Box::new(TreeElement::Partial(s[1..].to_string()))
|
||||||
|
} else {
|
||||||
|
Box::new(TreeElement::Number(s[1..].parse().unwrap()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if s.contains('_') {
|
||||||
|
TreeElement::Partial(s.to_string())
|
||||||
|
} else {
|
||||||
|
TreeElement::Number(s.parse().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
tokens.remove(i - 1);
|
let r = match r {
|
||||||
tokens.remove(i - 1);
|
InterTreeElement::Processed(x) => x.clone(),
|
||||||
tokens[i - 1] = Token::Number(v);
|
InterTreeElement::Unprocessed(Token::Value(s)) => {
|
||||||
|
if s.starts_with('-') {
|
||||||
|
TreeElement::Neg {
|
||||||
|
r: {
|
||||||
|
if s.contains('_') {
|
||||||
|
Box::new(TreeElement::Partial(s[1..].to_string()))
|
||||||
|
} else {
|
||||||
|
Box::new(TreeElement::Number(s[1..].parse().unwrap()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if s.contains('_') {
|
||||||
|
TreeElement::Partial(s.to_string())
|
||||||
|
} else {
|
||||||
|
TreeElement::Number(s.parse().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let v = match tree[i] {
|
||||||
|
InterTreeElement::Unprocessed(Token::OpAdd) => TreeElement::Add {
|
||||||
|
l: Box::new(l),
|
||||||
|
r: Box::new(r),
|
||||||
|
},
|
||||||
|
InterTreeElement::Unprocessed(Token::OpDiv) => TreeElement::Div {
|
||||||
|
l: Box::new(l),
|
||||||
|
r: Box::new(r),
|
||||||
|
},
|
||||||
|
InterTreeElement::Unprocessed(Token::OpMult) => TreeElement::Mul {
|
||||||
|
l: Box::new(l),
|
||||||
|
r: Box::new(r),
|
||||||
|
},
|
||||||
|
InterTreeElement::Unprocessed(Token::OpSub) => TreeElement::Sub {
|
||||||
|
l: Box::new(l),
|
||||||
|
r: Box::new(r),
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
tree.remove(i - 1);
|
||||||
|
tree.remove(i - 1);
|
||||||
|
tree[i - 1] = InterTreeElement::Processed(v);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -243,7 +455,14 @@ impl Board {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(tokens[0].val())
|
match tree.into_iter().next().unwrap() {
|
||||||
|
InterTreeElement::Processed(x) => x,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(&self) -> Option<f32> {
|
||||||
|
Self::treeify(&self.tokenize()).evaluate()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hacky method to parse a board from a string
|
/// Hacky method to parse a board from a string
|
||||||
|
@ -255,7 +474,9 @@ impl Board {
|
||||||
let x = s
|
let x = s
|
||||||
.chars()
|
.chars()
|
||||||
.filter_map(|c| {
|
.filter_map(|c| {
|
||||||
if let Some(symb) = Symb::from_char(&c) {
|
if c == '_' {
|
||||||
|
Some(None)
|
||||||
|
} else if let Some(symb) = Symb::from_char(&c) {
|
||||||
Some(Some((symb, current_player)))
|
Some(Some((symb, current_player)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
Loading…
Reference in New Issue