summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server/hand.cpp76
-rw-r--r--server/hand.h23
-rw-r--r--server/player.cpp55
3 files changed, 103 insertions, 51 deletions
diff --git a/server/hand.cpp b/server/hand.cpp
new file mode 100644
index 0000000..393a6c0
--- /dev/null
+++ b/server/hand.cpp
@@ -0,0 +1,76 @@
+#include "hand.h"
+
+bool Hand::agari(const Tiles& tiles) {
+ return basic_format(tiles);
+}
+
+bool Hand::basic_format(const Tiles& tiles, bool pair_eaten) {
+ if(!tiles) {
+ return true; // All tiles eaten.
+ }
+
+ if(!pair_eaten) {
+ if(Tiles rest = eat_pair(tiles)) {
+ if(basic_format(rest, true)) {
+ return true;
+ }
+ }
+ }
+
+ if(tiles.size() < 3) {
+ return false;
+ }
+
+ if(Tiles rest = eat_pon(tiles)) {
+ if(basic_format(rest, pair_eaten)) {
+ return true;
+ }
+ }
+
+ if(Tiles rest = eat_chi(tiles)) {
+ return basic_format(rest, pair_eaten);
+ }
+
+ return false;
+}
+
+Tiles Hand::eat_pair(Tiles tiles) {
+ if(tiles[0] == tiles[1]) {
+ tiles.erase(tiles.begin(), tiles.begin() + 2);
+ return tiles;
+ }
+ return Tiles();
+}
+
+Tiles Hand::eat_pon(Tiles tiles) {
+ if(tiles[0] == tiles[2]) {
+ tiles.erase(tiles.begin(), tiles.begin() + 3);
+ return tiles;
+ }
+ return Tiles();
+}
+
+Tiles Hand::eat_chi(Tiles tiles) {
+ Tile::Set set = tiles[0].get_set();
+ int num = tiles[0].get_num();
+
+ if(set == Tile::Honor || num > 7) {
+ // Can't be chi-ed.
+ return Tiles();
+ }
+
+ Tiles::iterator it1 = std::find(tiles.begin(), tiles.end(), Tile(set, num + 1));
+ if(it1 != tiles.end()) {
+ Tiles::iterator it2 = std::find(tiles.begin(), tiles.end(), Tile(set, num + 2));
+ if(it2 != tiles.end()) {
+ // We can eat a chi of tiles.
+ tiles.erase(it2);
+ tiles.erase(it1);
+ tiles.erase(tiles.begin());
+ return tiles;
+ }
+ }
+ return Tiles();
+}
+
+
diff --git a/server/hand.h b/server/hand.h
new file mode 100644
index 0000000..46ae40a
--- /dev/null
+++ b/server/hand.h
@@ -0,0 +1,23 @@
+#ifndef HAND_H
+#define HAND_H
+
+#include "../common/tile.h"
+
+namespace Hand {
+ //! Check if the tiles constitute a complete hand. Also valid for the concealed part of an open hand.
+ bool agari(const Tiles& tiles);
+
+ // Check if the tiles is matching the normal format of one pair and rest triplets.
+ bool basic_format(const Tiles& tiles, bool pair_eaten = false);
+
+ // Eat a pair from beginning of list if possible and return rest, else return empty list.
+ Tiles eat_pair(Tiles tiles);
+
+ // Eat a pon from beginning of list if possible and return rest, else return empty list.
+ Tiles eat_pon(Tiles tiles);
+
+ // Eat a chi from beginning of list if possible and return rest, else return empty list.
+ Tiles eat_chi(Tiles tiles);
+}
+
+#endif
diff --git a/server/player.cpp b/server/player.cpp
index 61f5831..4407095 100644
--- a/server/player.cpp
+++ b/server/player.cpp
@@ -1,5 +1,7 @@
#include "game.h"
+#include "hand.h"
+
void Game::Player::round_start() {
// Reset contents.
hand.clear();
@@ -151,64 +153,15 @@ bool Game::Player::can_kan(Tile tile, int target) {
return false;
}
-//! Recursive function that eats away sets from a hand to determine if it's valid or not.
-bool complete_hand(Tiles tiles, bool pair_eaten = false) {
- if(!tiles) {
- return true; // All tiles eaten.
- }
-
- if(tiles[0] == tiles[1]) {
- if(!pair_eaten) {
- // We can eat a pair of tiles.
- Tiles t = tiles;
- t.erase(t.begin(), t.begin() + 2);
- if(complete_hand(t, true)) {
- return true;
- }
- }
-
- if(tiles[1] == tiles[2]) {
- // We can eat a pon of tiles.
- Tiles t = tiles;
- t.erase(t.begin(), t.begin() + 3);
- if(complete_hand(t, pair_eaten)) {
- return true;
- }
- }
- }
-
- Tile::Set set = tiles[0].get_set();
- int num = tiles[0].get_num();
-
- if(set == Tile::Honor || num > 7) {
- // Can't be chi-ed.
- return false;
- }
-
- Tiles::iterator it1 = std::find(tiles.begin(), tiles.end(), Tile(set, num + 1));
- if(it1 != tiles.end()) {
- Tiles::iterator it2 = std::find(tiles.begin(), tiles.end(), Tile(set, num + 2));
- if(it2 != tiles.end()) {
- // We can eat a chi of tiles.
- tiles.erase(it2);
- tiles.erase(it1);
- tiles.erase(tiles.begin());
- return complete_hand(tiles, pair_eaten);
- }
- }
-
- return false;
-}
-
bool Game::Player::can_tsumo() {
- return complete_hand(hand);
+ return Hand::agari(hand);
}
bool Game::Player::can_ron(Tile tile) {
Tiles tiles = hand;
tiles.push_back(tile);
tiles.sort();
- return complete_hand(tiles);
+ return Hand::agari(tiles);
}
void Game::Player::draw(Tile tile) {