2006-01-01 20:36:16 +01:00
|
|
|
/*****************************************************************************
|
2008-01-08 14:52:32 +01:00
|
|
|
* Eliot
|
2008-07-20 14:15:51 +02:00
|
|
|
* Copyright (C) 2002-2008 Antoine Fraboulet & Olivier Teulière
|
2008-01-08 14:52:32 +01:00
|
|
|
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
|
|
|
* Olivier Teulière <ipkiss @@ gmail.com>
|
2006-01-01 20:36:16 +01:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2008-07-20 14:15:51 +02:00
|
|
|
#include <cstring>
|
|
|
|
#include <cstdlib> // For atoi
|
2011-01-30 00:47:20 +01:00
|
|
|
#include <cwctype> // For iswlower
|
2009-11-29 17:01:31 +01:00
|
|
|
#include <cstdio>
|
2006-01-01 20:36:16 +01:00
|
|
|
|
2008-01-08 14:52:32 +01:00
|
|
|
#include "dic.h"
|
2006-01-01 20:36:16 +01:00
|
|
|
#include "pldrack.h"
|
|
|
|
#include "round.h"
|
|
|
|
#include "turn.h"
|
|
|
|
#include "player.h"
|
2008-01-28 20:17:33 +01:00
|
|
|
#include "ai_percent.h"
|
2011-08-27 19:21:26 +02:00
|
|
|
#include "game_params.h"
|
2006-01-01 20:36:16 +01:00
|
|
|
#include "game.h"
|
|
|
|
#include "game_factory.h"
|
2008-01-08 14:52:32 +01:00
|
|
|
#include "training.h"
|
|
|
|
#include "freegame.h"
|
|
|
|
#include "duplicate.h"
|
2006-01-22 13:23:52 +01:00
|
|
|
#include "encoding.h"
|
2008-09-13 23:32:45 +02:00
|
|
|
#include "game_exception.h"
|
2008-11-23 09:18:03 +01:00
|
|
|
#include "game_move_cmd.h"
|
2006-01-01 20:36:16 +01:00
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
/*************************
|
|
|
|
* Ident string used to identify saved Eliot games
|
|
|
|
*************************/
|
|
|
|
#define IDENT_STRING "Eliot"
|
|
|
|
#define IDENT_FORMAT_14 ""
|
|
|
|
#define IDENT_FORMAT_15 "1.5"
|
|
|
|
|
|
|
|
|
|
|
|
/********************************************************
|
|
|
|
*
|
|
|
|
* Loading games
|
|
|
|
*
|
|
|
|
********************************************************/
|
|
|
|
|
|
|
|
Game * Game::load(FILE *fin, const Dictionary& iDic)
|
|
|
|
{
|
|
|
|
char buff[4096];
|
|
|
|
// 10d is \012
|
|
|
|
// 13d is \015
|
|
|
|
char delim[] = " \t\n\012\015|";
|
|
|
|
char *token;
|
|
|
|
|
|
|
|
// Check characteristic string
|
|
|
|
if (fgets(buff, sizeof(buff), fin) == NULL)
|
|
|
|
{
|
2008-09-13 23:32:45 +02:00
|
|
|
throw GameException("Cannot recognize the first line");
|
2006-01-01 20:36:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((token = strtok(buff, delim)) == NULL)
|
|
|
|
{
|
2008-09-13 23:32:45 +02:00
|
|
|
throw GameException("The first line is empty");
|
2006-01-01 20:36:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* checks for IDENT_STRING and file format */
|
|
|
|
if (string(token) != IDENT_STRING)
|
|
|
|
{
|
2008-09-13 23:32:45 +02:00
|
|
|
throw GameException("Invalid identity string: " + string(token));
|
2006-01-01 20:36:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((token = strtok(NULL, delim)) == NULL)
|
2006-01-22 13:23:52 +01:00
|
|
|
{
|
|
|
|
return Game::gameLoadFormat_14(fin,iDic);
|
|
|
|
}
|
2006-01-01 20:36:16 +01:00
|
|
|
|
|
|
|
if (string(token) == string(IDENT_FORMAT_15))
|
2006-01-22 13:23:52 +01:00
|
|
|
{
|
|
|
|
return Game::gameLoadFormat_15(fin,iDic);
|
|
|
|
}
|
2006-01-01 20:36:16 +01:00
|
|
|
|
2008-09-13 23:32:45 +02:00
|
|
|
throw GameException("Unknown format: " + string(token));
|
2006-01-01 20:36:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Game* Game::gameLoadFormat_14(FILE *fin, const Dictionary& iDic)
|
|
|
|
{
|
2006-08-12 00:13:02 +02:00
|
|
|
int ret = 0;
|
|
|
|
int line = 0;
|
|
|
|
Tile tile;
|
|
|
|
char buff[4096];
|
|
|
|
char rack[20];
|
|
|
|
char word[20];
|
|
|
|
char pos [5];
|
|
|
|
char delim[]=" \t\n\012\015";
|
|
|
|
char *token;
|
|
|
|
Game *pGame = NULL;
|
2008-01-08 14:52:32 +01:00
|
|
|
|
2011-08-28 18:44:34 +02:00
|
|
|
pGame = GameFactory::Instance()->createGame(GameParams(iDic, GameParams::kTRAINING));
|
2009-11-29 17:01:31 +01:00
|
|
|
pGame->addPlayer(new HumanPlayer);
|
2006-08-12 00:13:02 +02:00
|
|
|
pGame->start();
|
2008-01-08 14:52:32 +01:00
|
|
|
|
2006-08-12 00:13:02 +02:00
|
|
|
/* rack word ?bonus pts coord */
|
|
|
|
/* EUOFMIE FUMEE * 26 H 4 */
|
2008-01-08 14:52:32 +01:00
|
|
|
|
2006-08-12 00:13:02 +02:00
|
|
|
/* read all turns until total */
|
|
|
|
while (fgets(buff, sizeof(buff), fin))
|
|
|
|
{
|
|
|
|
line++;
|
|
|
|
token = strtok(buff, delim);
|
|
|
|
if (token != NULL)
|
|
|
|
{
|
|
|
|
if (strcmp(token, "total") == 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* rack */
|
|
|
|
strncpy(rack, token, sizeof(rack));
|
2011-07-30 21:48:05 +02:00
|
|
|
static_cast<Training*>(pGame)->setRackManual(false, wfl(rack));
|
2008-09-13 23:32:45 +02:00
|
|
|
|
2006-08-12 00:13:02 +02:00
|
|
|
/* word */
|
|
|
|
token = strtok(NULL, delim);
|
|
|
|
if (!token || strcmp(token, "total") == 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
strncpy(word, token, sizeof(word));
|
2008-09-13 23:32:45 +02:00
|
|
|
|
2006-08-12 00:13:02 +02:00
|
|
|
/* bonus */
|
|
|
|
if ((token = strtok(NULL, delim)) == NULL)
|
|
|
|
break;
|
2008-09-13 23:32:45 +02:00
|
|
|
|
2006-08-12 00:13:02 +02:00
|
|
|
/* points */
|
|
|
|
if (token[0] == '*')
|
|
|
|
{
|
|
|
|
if ((token = strtok(NULL, delim)) == NULL)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pos 1 */
|
|
|
|
if ((token = strtok(NULL, delim)) == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
//debug("(%s ", token);
|
|
|
|
strncpy(pos, token, sizeof(pos));
|
|
|
|
|
|
|
|
/* pos 2 */
|
|
|
|
if ((token = strtok(NULL, delim)) == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
//debug("%s)", token);
|
2009-03-18 19:53:57 +01:00
|
|
|
strncat(pos, token, sizeof(pos) - strlen(pos) - 1);
|
2006-08-12 00:13:02 +02:00
|
|
|
|
2011-07-30 21:48:05 +02:00
|
|
|
if ((ret = pGame->play(wfl(pos), wfl(word))))
|
2006-08-12 00:13:02 +02:00
|
|
|
{
|
|
|
|
GameFactory::Instance()->releaseGame(*pGame);
|
2008-09-13 23:32:45 +02:00
|
|
|
char tmp1[10];
|
|
|
|
snprintf(tmp1, 10, "%d", ret);
|
|
|
|
char tmp2[10];
|
|
|
|
snprintf(tmp2, 10, "%d", ret);
|
|
|
|
throw GameException("Loading error " + string(tmp1) +
|
|
|
|
" on line " + string(tmp2));
|
2006-08-12 00:13:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return pGame;
|
2006-01-01 20:36:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Game* Game::gameLoadFormat_15(FILE *fin, const Dictionary& iDic)
|
|
|
|
{
|
|
|
|
Game *pGame = NULL;
|
|
|
|
|
|
|
|
char buff[4096];
|
|
|
|
char *pos;
|
|
|
|
|
|
|
|
/*************/
|
|
|
|
/* Game type */
|
|
|
|
/*************/
|
|
|
|
|
|
|
|
while (fgets(buff, sizeof(buff), fin))
|
|
|
|
{
|
|
|
|
// Indication of game type
|
|
|
|
pos = strstr(buff, "Game type: ");
|
|
|
|
if (pos != NULL)
|
|
|
|
{
|
|
|
|
// No Game object should have been created yet
|
|
|
|
if (pGame != NULL)
|
|
|
|
{
|
|
|
|
delete pGame;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
// Create the correct Game object
|
2011-08-28 18:20:00 +02:00
|
|
|
GameParams::GameMode mode;
|
2006-01-01 20:36:16 +01:00
|
|
|
if (strstr(buff, "Training"))
|
2011-08-28 18:20:00 +02:00
|
|
|
mode = GameParams::kTRAINING;
|
2006-01-01 20:36:16 +01:00
|
|
|
else if (strstr(buff, "Free game"))
|
2011-08-28 18:20:00 +02:00
|
|
|
mode = GameParams::kFREEGAME;
|
2006-01-01 20:36:16 +01:00
|
|
|
else if (strstr(buff, "Duplicate"))
|
2011-08-28 18:20:00 +02:00
|
|
|
mode = GameParams::kDUPLICATE;
|
2012-03-05 01:27:56 +01:00
|
|
|
else if (strstr(buff, "Arbitration"))
|
|
|
|
mode = GameParams::kARBITRATION;
|
2006-01-01 20:36:16 +01:00
|
|
|
else
|
2008-09-13 23:32:45 +02:00
|
|
|
throw GameException("Unknown game type");
|
2011-08-28 18:20:00 +02:00
|
|
|
|
2011-08-28 18:44:34 +02:00
|
|
|
pGame = GameFactory::Instance()->createGame(GameParams(iDic, mode));
|
2006-01-01 20:36:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************/
|
|
|
|
/* Player List */
|
|
|
|
/***************/
|
|
|
|
|
|
|
|
while (fgets(buff, sizeof(buff), fin))
|
|
|
|
{
|
|
|
|
// Players type
|
|
|
|
pos = strstr(buff, "Player ");
|
|
|
|
if (pos != NULL)
|
|
|
|
{
|
|
|
|
int nb = 0;
|
|
|
|
char type[20];
|
|
|
|
if (sscanf(pos, "Player %d: %19s", &nb, type) > 1)
|
|
|
|
{
|
|
|
|
if (string(type) == "Human")
|
|
|
|
{
|
2008-01-28 20:17:33 +01:00
|
|
|
pGame->addPlayer(new HumanPlayer);
|
2006-01-01 20:36:16 +01:00
|
|
|
}
|
|
|
|
else if (string(type) == "Computer")
|
|
|
|
{
|
2011-08-28 18:20:00 +02:00
|
|
|
if (pGame->getMode() == GameParams::kTRAINING)
|
2006-01-01 20:36:16 +01:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-01-28 20:17:33 +01:00
|
|
|
pGame->addPlayer(new AIPercent(1));
|
2006-01-22 13:23:52 +01:00
|
|
|
}
|
2006-01-01 20:36:16 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete pGame;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strstr(buff," N | RACK "))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************/
|
|
|
|
/* Turn list */
|
|
|
|
/*************/
|
|
|
|
|
|
|
|
while (fgets(buff, sizeof(buff), fin))
|
|
|
|
{
|
|
|
|
// Skip columns title
|
|
|
|
if (strstr(buff,"| PTS | P |") != NULL)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip columns title
|
|
|
|
if (strstr(buff, "==") != NULL)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (string(buff) == "\n")
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-01-08 14:52:32 +01:00
|
|
|
int num;
|
|
|
|
char rack[20];
|
|
|
|
char tmpWord[20];
|
|
|
|
char ref[4];
|
|
|
|
int pts;
|
|
|
|
unsigned int player;
|
2006-01-01 20:36:16 +01:00
|
|
|
char bonus = 0;
|
2008-01-11 11:09:26 +01:00
|
|
|
int res = sscanf(buff, " %2d | %8s | %s | %3s | %3d | %1u | %c",
|
2008-01-08 14:52:32 +01:00
|
|
|
&num, rack, tmpWord, ref, &pts, &player, &bonus);
|
2006-01-22 13:23:52 +01:00
|
|
|
|
2006-01-01 20:36:16 +01:00
|
|
|
if (res < 6)
|
2006-01-22 13:23:52 +01:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-09-13 23:32:45 +02:00
|
|
|
//debug(" %2d | %8s | %s | %3s | %3d | %1d | %c \n",
|
|
|
|
// num, rack, tmpWord, ref, pts, player, bonus);
|
2006-01-22 13:23:52 +01:00
|
|
|
|
2006-01-01 20:36:16 +01:00
|
|
|
// Integrity checks
|
|
|
|
// TODO: add more checks
|
|
|
|
if (pts < 0)
|
2006-01-22 13:23:52 +01:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2008-01-08 14:52:32 +01:00
|
|
|
if (player > pGame->getNPlayers())
|
2006-01-22 13:23:52 +01:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2006-01-01 20:36:16 +01:00
|
|
|
if (bonus && bonus != '*')
|
2006-01-22 13:23:52 +01:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2006-01-01 20:36:16 +01:00
|
|
|
// Build a rack for the correct player
|
|
|
|
PlayedRack pldrack;
|
2011-07-30 21:48:05 +02:00
|
|
|
if (!iDic.validateLetters(wfl(rack)))
|
2006-01-01 20:36:16 +01:00
|
|
|
{
|
2008-09-13 23:32:45 +02:00
|
|
|
throw GameException("Rack invalid for the current dictionary");
|
2006-01-01 20:36:16 +01:00
|
|
|
}
|
2011-07-30 21:48:05 +02:00
|
|
|
pldrack.setManual(wfl(rack));
|
2006-01-01 20:36:16 +01:00
|
|
|
|
|
|
|
// Build a round
|
|
|
|
Round round;
|
|
|
|
round.setPoints(pts);
|
|
|
|
if (bonus == '*')
|
|
|
|
round.setBonus(1);
|
2006-01-22 13:23:52 +01:00
|
|
|
|
2011-07-30 21:48:05 +02:00
|
|
|
wstring word = wfl(tmpWord);
|
2008-01-08 14:52:32 +01:00
|
|
|
Tile tile;
|
2006-01-01 20:36:16 +01:00
|
|
|
if (isalpha(ref[0]))
|
2006-01-22 13:23:52 +01:00
|
|
|
{
|
|
|
|
// Horizontal word
|
|
|
|
round.accessCoord().setDir(Coord::HORIZONTAL);
|
|
|
|
round.accessCoord().setRow(ref[0] - 'A' + 1);
|
|
|
|
round.accessCoord().setCol(atoi(ref + 1));
|
|
|
|
|
2008-01-08 14:52:32 +01:00
|
|
|
for (unsigned int i = 0; i < word.size(); i++)
|
2006-01-01 20:36:16 +01:00
|
|
|
{
|
2006-01-22 13:23:52 +01:00
|
|
|
tile = Tile(word[i]);
|
|
|
|
|
|
|
|
if (!pGame->m_board.getTile(round.getCoord().getRow(), round.getCoord().getCol() + i).isEmpty())
|
|
|
|
{
|
|
|
|
round.addRightFromBoard(tile);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-01-08 14:52:32 +01:00
|
|
|
round.addRightFromRack(tile, iswlower(word[i]));
|
|
|
|
pGame->m_bag.takeTile((iswlower(word[i])) ? Tile::Joker() : tile);
|
2006-01-22 13:23:52 +01:00
|
|
|
}
|
2006-01-01 20:36:16 +01:00
|
|
|
}
|
2006-01-22 13:23:52 +01:00
|
|
|
}
|
2006-01-01 20:36:16 +01:00
|
|
|
else
|
2006-01-22 13:23:52 +01:00
|
|
|
{
|
|
|
|
// Vertical word
|
|
|
|
round.accessCoord().setDir(Coord::VERTICAL);
|
|
|
|
round.accessCoord().setRow(ref[strlen(ref) - 1] - 'A' + 1);
|
|
|
|
round.accessCoord().setCol(atoi(ref));
|
|
|
|
|
2008-01-08 14:52:32 +01:00
|
|
|
for (unsigned int i = 0; i < word.size(); i++)
|
2006-01-01 20:36:16 +01:00
|
|
|
{
|
2006-01-22 13:23:52 +01:00
|
|
|
tile = Tile(word[i]);
|
|
|
|
|
|
|
|
if (!pGame->m_board.getTile(round.getCoord().getRow() + i, round.getCoord().getCol()).isEmpty())
|
|
|
|
{
|
|
|
|
round.addRightFromBoard(tile);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-01-08 14:52:32 +01:00
|
|
|
round.addRightFromRack(tile, iswlower(word[i]));
|
|
|
|
pGame->m_bag.takeTile((iswlower(word[i])) ? Tile::Joker() : tile);
|
2006-01-22 13:23:52 +01:00
|
|
|
}
|
2006-01-01 20:36:16 +01:00
|
|
|
}
|
2006-01-22 13:23:52 +01:00
|
|
|
}
|
|
|
|
|
2008-01-08 14:52:32 +01:00
|
|
|
// pGame->m_currPlayer = player;
|
|
|
|
// // Update the rack for the player
|
|
|
|
// pGame->m_players[player]->setCurrentRack(pldrack);
|
|
|
|
// // End the turn for the current player (this creates a new rack)
|
|
|
|
// pGame->m_players[player]->endTurn(round,num - 1);
|
2006-01-22 13:23:52 +01:00
|
|
|
|
2006-01-01 20:36:16 +01:00
|
|
|
// Play the round
|
2009-11-29 17:01:31 +01:00
|
|
|
GameMoveCmd cmd(*pGame, Move(round), pGame->m_currPlayer);
|
2008-11-23 09:18:03 +01:00
|
|
|
cmd.execute();
|
2006-01-01 20:36:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************/
|
|
|
|
/* End of turn list, switching to ... */
|
|
|
|
/**************************************/
|
|
|
|
|
|
|
|
// Last racks
|
|
|
|
pos = strstr(buff, "Rack ");
|
|
|
|
if (pos != NULL && pGame != NULL)
|
|
|
|
{
|
|
|
|
int nb = 0;
|
|
|
|
char letters[20];
|
|
|
|
if (sscanf(pos, "Rack %d: %19s", &nb, letters) > 1)
|
|
|
|
{
|
|
|
|
// Create the played rack
|
|
|
|
PlayedRack pldrack;
|
2011-07-30 21:48:05 +02:00
|
|
|
pldrack.setManual(wfl(letters));
|
2006-01-01 20:36:16 +01:00
|
|
|
// Give the rack to the player
|
|
|
|
pGame->m_players[nb]->setCurrentRack(pldrack);
|
|
|
|
}
|
|
|
|
// Read next line
|
|
|
|
// continue;
|
|
|
|
}
|
2006-01-22 13:23:52 +01:00
|
|
|
|
|
|
|
|
2006-01-01 20:36:16 +01:00
|
|
|
// Finalize the game
|
|
|
|
if (pGame)
|
|
|
|
{
|
|
|
|
// We don't really know whose turn it is, but at least we know that
|
|
|
|
// the game was saved while a human was to play.
|
2008-01-08 14:52:32 +01:00
|
|
|
for (unsigned int i = 0; i < pGame->getNPlayers(); i++)
|
2006-01-01 20:36:16 +01:00
|
|
|
{
|
|
|
|
if (pGame->m_players[i]->isHuman())
|
|
|
|
{
|
|
|
|
pGame->m_currPlayer = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return pGame;
|
|
|
|
}
|
|
|
|
|