github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/sdk/examples/xo_rust/src/handler/game.rs (about)

     1  /*
     2   * Copyright 2018 Bitwise IO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   * -----------------------------------------------------------------------------
    16   */
    17  
    18  use std::collections::HashMap;
    19  
    20  use sawtooth_sdk::processor::handler::ApplyError;
    21  
    22  const POSSIBLE_WINS: [(usize, usize, usize); 8] = [
    23      (1, 2, 3),
    24      (4, 5, 6),
    25      (7, 8, 9),
    26      (1, 4, 7),
    27      (2, 5, 8),
    28      (3, 6, 9),
    29      (1, 5, 9),
    30      (3, 5, 7),
    31  ];
    32  
    33  #[derive(Debug, Clone)]
    34  pub struct Game {
    35      name: String,
    36      board: String,
    37      game_state: String,
    38      player1: String,
    39      player2: String,
    40      player1_short: String,
    41      player2_short: String,
    42  }
    43  
    44  impl Game {
    45      pub fn new(name: String) -> Game {
    46          Game {
    47              name: name,
    48              board: "-".repeat(9),
    49              game_state: String::from("P1-NEXT"),
    50              player1: String::from(""),
    51              player2: String::from(""),
    52              player1_short: String::from(""),
    53              player2_short: String::from(""),
    54          }
    55      }
    56  
    57      fn to_string(&self) -> String {
    58          let fields = vec![
    59              self.name.clone(),
    60              self.board.clone(),
    61              self.game_state.clone(),
    62              self.player1.clone(),
    63              self.player2.clone(),
    64          ];
    65          fields.join(",")
    66      }
    67  
    68      fn from_string(game_string: String) -> Option<Game> {
    69          let items: Vec<&str> = game_string.split(",").collect();
    70          if items.len() != 5 {
    71              return None;
    72          }
    73          let mut g = Game {
    74              name: items[0].to_string(),
    75              board: items[1].to_string(),
    76              game_state: items[2].to_string(),
    77              player1: String::from(""),
    78              player2: String::from(""),
    79              player1_short: String::from(""),
    80              player2_short: String::from(""),
    81          };
    82          g.set_player1(items[3]);
    83          g.set_player2(items[4]);
    84          Some(g)
    85      }
    86  
    87      pub fn serialize_games(games: HashMap<String, Game>) -> String {
    88          let mut game_strings: Vec<String> = vec![];
    89          for (_, game) in games {
    90              game_strings.push(game.to_string().clone());
    91          }
    92          game_strings.sort();
    93          game_strings.join("|")
    94      }
    95  
    96      pub fn deserialize_games(games_string: String) -> Option<HashMap<String, Game>> {
    97          let mut ret: HashMap<String, Game> = HashMap::new();
    98          let game_string_list: Vec<&str> = games_string.split("|").collect();
    99          for g in game_string_list {
   100              let game = Game::from_string(g.to_string());
   101              match game {
   102                  Some(game_item) => ret.insert(game_item.name.clone(), game_item),
   103                  None => return None,
   104              };
   105          }
   106          Some(ret)
   107      }
   108  
   109      pub fn mark_space(&mut self, space: usize) -> Result<(), ApplyError> {
   110          let mark = match self.game_state.as_str() {
   111              "P1-NEXT" => "X",
   112              "P2-NEXT" => "O",
   113              other_state => {
   114                  return Err(ApplyError::InvalidTransaction(String::from(format!(
   115                      "Invalid state {}",
   116                      other_state
   117                  ))))
   118              }
   119          };
   120  
   121          let index = space - 1;
   122  
   123          let board_vec: Vec<String> = self.board
   124              .chars()
   125              .enumerate()
   126              .map(|(i, ch)| {
   127                  if i == index {
   128                      mark.to_string()
   129                  } else {
   130                      ch.to_string()
   131                  }
   132              })
   133              .collect();
   134          self.board = board_vec.join("");
   135          Ok(())
   136      }
   137  
   138      pub fn update_state(&mut self) -> Result<(), ApplyError> {
   139          let x_wins = self.is_win("X");
   140          let o_wins = self.is_win("O");
   141  
   142          let winner = match (x_wins, o_wins) {
   143              (true, true) => {
   144                  return Err(ApplyError::InvalidTransaction(String::from(
   145                      "Two winners (there can only be one)",
   146                  )))
   147              }
   148              (true, false) => Some(String::from("P1-WIN")),
   149              (false, true) => Some(String::from("P2-WIN")),
   150              _ => None,
   151          };
   152  
   153          if let Some(w) = winner {
   154              self.game_state = w;
   155              return Ok(());
   156          }
   157  
   158          if !self.board.contains("-") {
   159              self.game_state = String::from("TIE");
   160              return Ok(());
   161          }
   162  
   163          if self.game_state.as_str() == "P1-NEXT" {
   164              self.game_state = String::from("P2-NEXT");
   165              return Ok(());
   166          }
   167  
   168          if self.game_state.as_str() == "P2-NEXT" {
   169              self.game_state = String::from("P1-NEXT");
   170              return Ok(());
   171          }
   172  
   173          Err(ApplyError::InvalidTransaction(String::from(format!(
   174              "Unhandled state: {}",
   175              self.game_state
   176          ))))
   177      }
   178  
   179      pub fn is_win(&self, letter: &str) -> bool {
   180          let letter = letter.to_string();
   181          for (i1, i2, i3) in POSSIBLE_WINS.iter() {
   182              let board_chars: Vec<char> = self.board.chars().collect();
   183              if board_chars[*i1 - 1].to_string() == letter
   184                  && board_chars[*i2 - 1].to_string() == letter
   185                  && board_chars[*i3 - 1].to_string() == letter
   186              {
   187                  return true;
   188              }
   189          }
   190          false
   191      }
   192  
   193      pub fn display(&self) {
   194          let b: Vec<char> = self.board.chars().collect();
   195          info!(
   196              "
   197      GAME: {}
   198      PLAYER 1: {}
   199      PLAYER 2: {}
   200      STATE: {}
   201  
   202       {} | {} | {}
   203      ---|---|---
   204       {} | {} | {}
   205      ---|---|---
   206       {} | {} | {}
   207      ",
   208              self.name,
   209              self.player1,
   210              self.player2,
   211              self.game_state,
   212              b[0],
   213              b[1],
   214              b[2],
   215              b[3],
   216              b[4],
   217              b[5],
   218              b[6],
   219              b[7],
   220              b[8]
   221          );
   222      }
   223  
   224      pub fn get_state(&self) -> String {
   225          self.game_state.clone()
   226      }
   227  
   228      pub fn get_player1(&self) -> String {
   229          self.player1.clone()
   230      }
   231  
   232      pub fn get_player2(&self) -> String {
   233          self.player2.clone()
   234      }
   235  
   236      pub fn get_board(&self) -> String {
   237          self.board.clone()
   238      }
   239  
   240      pub fn set_player1(&mut self, p1: &str) {
   241          self.player1 = p1.to_string();
   242          if p1.len() > 6 {
   243              self.player1_short = p1[..6].to_string();
   244          } else {
   245              self.player1_short = String::from(p1);
   246          }
   247      }
   248  
   249      pub fn set_player2(&mut self, p2: &str) {
   250          self.player2 = p2.to_string();
   251          if p2.len() > 6 {
   252              self.player2_short = p2[..6].to_string();
   253          } else {
   254              self.player2_short = String::from(p2);
   255          }
   256      }
   257  }
   258  
   259  impl PartialEq for Game {
   260      fn eq(&self, other: &Self) -> bool {
   261          self.name == other.name && self.game_state == other.board
   262              && self.game_state == other.game_state && self.player1 == other.player1
   263              && self.player2 == other.player2
   264      }
   265  }