mirror of
git://git.savannah.nongnu.org/eliot.git
synced 2025-01-30 20:34:27 +01:00
720 lines
16 KiB
C
720 lines
16 KiB
C
/* Eliot */
|
|
/* Copyright (C) 1999 Antoine Fraboulet */
|
|
/* antoine.fraboulet@free.fr */
|
|
/* */
|
|
/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
|
/* $Id: game.c,v 1.1 2004/04/08 09:43:06 afrab Exp $ */
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
#include "dic.h"
|
|
#include "tiles.h"
|
|
#include "bag.h"
|
|
#include "rack.h"
|
|
#include "round.h"
|
|
#include "pldrack.h"
|
|
#include "results.h"
|
|
#include "board.h"
|
|
#include "game.h"
|
|
#include "gameio.h"
|
|
#include "game_internals.h"
|
|
|
|
#include "debug.h"
|
|
|
|
/*********************************************************
|
|
*********************************************************/
|
|
|
|
Game
|
|
Game_create(Dictionary dic)
|
|
{
|
|
int i;
|
|
Game g;
|
|
|
|
g = (Game)malloc(sizeof(struct tgame));
|
|
if (g == NULL)
|
|
return NULL;
|
|
|
|
g->dic = dic;
|
|
g->bag = Bag_create();
|
|
g->board = Board_create();
|
|
g->searchresults = Results_create();
|
|
|
|
for(i=0; i < PLAYEDRACK_MAX; i++)
|
|
{
|
|
g->playedrounds[i] = Round_create();
|
|
g->playedracks[i] = Playedrack_create();
|
|
}
|
|
|
|
Game_init(g);
|
|
return g;
|
|
}
|
|
|
|
|
|
void
|
|
Game_destroy(Game g)
|
|
{
|
|
int i;
|
|
if (g) {
|
|
Bag_destroy(g->bag);
|
|
Board_destroy(g->board);
|
|
Results_destroy(g->searchresults);
|
|
for(i=0; i < PLAYEDRACK_MAX; i++) {
|
|
Round_destroy(g->playedrounds[i]);
|
|
Playedrack_destroy(g->playedracks[i]);
|
|
}
|
|
free(g);
|
|
}
|
|
}
|
|
|
|
/*********************************************************
|
|
*********************************************************/
|
|
|
|
void
|
|
Game_init(Game g)
|
|
{
|
|
int i;
|
|
Bag_init(g->bag);
|
|
Board_init(g->board);
|
|
Results_init(g->searchresults);
|
|
for(i=0; i < PLAYEDRACK_MAX; i++)
|
|
{
|
|
Round_init(g->playedrounds[i]);
|
|
Playedrack_init(g->playedracks[i]);
|
|
}
|
|
g->points = 0;
|
|
g->nrounds = 0;
|
|
}
|
|
|
|
|
|
int
|
|
Game_load(Game game, FILE* fin)
|
|
{
|
|
return Game_read_game(fin,game);
|
|
}
|
|
|
|
|
|
void
|
|
Game_save(Game game, FILE* fout)
|
|
{
|
|
Game_print_game(fout,game);
|
|
}
|
|
|
|
|
|
/*********************************************************
|
|
*********************************************************/
|
|
|
|
|
|
Dictionary
|
|
Game_getdic(Game g)
|
|
{
|
|
return g->dic;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
Game_setdic(Game g, Dictionary d)
|
|
{
|
|
g->dic = d;
|
|
}
|
|
|
|
|
|
/*********************************************************
|
|
*********************************************************/
|
|
|
|
int
|
|
Game_search(Game game)
|
|
{
|
|
Rack rack;
|
|
if (game->dic == NULL)
|
|
return 1;
|
|
|
|
Game_removetestplay(game);
|
|
Results_init(game->searchresults);
|
|
|
|
rack = Rack_create();
|
|
Playedrack_getrack(game->playedracks[game->nrounds],rack);
|
|
if (game->nrounds)
|
|
Board_search(game->dic,game->board,
|
|
rack,
|
|
game->searchresults);
|
|
else
|
|
Board_search_first(game->dic,game->board,
|
|
rack,
|
|
game->searchresults);
|
|
Rack_destroy(rack);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
This function returns a rack "dest" with the unplayed tiles from
|
|
the current round.
|
|
03 sept 2000 : We have to sort the tiles according to the new rules
|
|
*/
|
|
static void
|
|
Game_restfromround(Playedrack dest, Playedrack source, Round round)
|
|
{
|
|
tile_t i;
|
|
Rack r;
|
|
|
|
r = Rack_create();
|
|
Playedrack_getrack(source,r);
|
|
|
|
/* we remove the played tiles from rack r */
|
|
for(i=0; i < Round_wordlen(round); i++)
|
|
{
|
|
if (Round_playedfromrack(round,i))
|
|
{
|
|
if (Round_joker(round,i))
|
|
Rack_remove(r,(tile_t)JOKER_TILE);
|
|
else
|
|
Rack_remove(r,Round_gettile(round,i));
|
|
}
|
|
}
|
|
|
|
Playedrack_init(dest);
|
|
Playedrack_setold(dest,r);
|
|
Rack_destroy(r);
|
|
}
|
|
|
|
|
|
int
|
|
Game_play(Game game, int n)
|
|
{
|
|
/*
|
|
* We remove tiles from the bag only when they are played
|
|
* on the board. When going back in the game, we must only
|
|
* replace played tiles.
|
|
* We test a rack when it is set but tiles are left in
|
|
* the bag.
|
|
*/
|
|
|
|
int i;
|
|
Round round;
|
|
|
|
if (game->dic == NULL)
|
|
return 1;
|
|
|
|
if ((round = Results_get(game->searchresults,n))==NULL)
|
|
return 2;
|
|
|
|
Round_copy(game->playedrounds[game->nrounds],round);
|
|
Board_addround(game->dic,game->board,round);
|
|
game->points += Round_points(round);
|
|
Game_restfromround(game->playedracks[game->nrounds + 1],
|
|
game->playedracks[game->nrounds],
|
|
round);
|
|
for(i=0; i < Round_wordlen(round); i++) {
|
|
if (Round_playedfromrack(round,i)) {
|
|
if (Round_joker(round,i))
|
|
Bag_taketile(game->bag,(tile_t)JOKER_TILE);
|
|
else
|
|
Bag_taketile(game->bag,Round_gettile(round,i));
|
|
}
|
|
}
|
|
game->nrounds++;
|
|
Results_init(game->searchresults);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
Game_back(Game game, int n)
|
|
{
|
|
int i,j;
|
|
Round lastround;
|
|
|
|
if (game->dic == NULL)
|
|
return 1;
|
|
|
|
for(i=0 ; i < n; i++) {
|
|
if (game->nrounds) {
|
|
game->nrounds--;
|
|
lastround = game->playedrounds[game->nrounds];
|
|
game->points -= Round_points(lastround);
|
|
Board_removeround(game->dic,game->board,lastround);
|
|
for(j=0; j < Round_wordlen(lastround); j++) {
|
|
if (Round_playedfromrack(lastround,j)) {
|
|
if (Round_joker(lastround,j))
|
|
Bag_replacetile(game->bag,JOKER_TILE);
|
|
else
|
|
Bag_replacetile(game->bag,Round_gettile(lastround,j));
|
|
}
|
|
}
|
|
Round_init(lastround);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Game_testplay(Game game, int n)
|
|
{
|
|
Round round;
|
|
|
|
if ((round = Results_get(game->searchresults,n))==NULL)
|
|
return 2;
|
|
Board_testround(game->board,round);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Game_removetestplay(Game game)
|
|
{
|
|
Board_removetestround(game->board);
|
|
return 0;
|
|
}
|
|
|
|
/*********************************************************
|
|
*********************************************************/
|
|
|
|
int
|
|
Game_getpoints(Game g)
|
|
{
|
|
return g->points;
|
|
}
|
|
|
|
char
|
|
Game_getboardchar(Game g, int r, int c)
|
|
{
|
|
tile_t t;
|
|
char l = 0;
|
|
t = Board_tile(g->board,r,c);
|
|
if (t)
|
|
{
|
|
l = codetochar(t);
|
|
if (Board_joker(g->board,r,c))
|
|
l = tolower(l);
|
|
}
|
|
return l;
|
|
}
|
|
|
|
int
|
|
Game_getboardcharattr(Game g,int r,int c)
|
|
{
|
|
int t = Board_gettestchar(g->board,r,c);
|
|
int j = Board_joker(g->board,r,c);
|
|
return (t << 1) | j;
|
|
}
|
|
|
|
int
|
|
Game_getboardwordmultiplier(Game g,int r,int c)
|
|
{
|
|
return Board_getwordmultiplier(g->board,r,c);
|
|
}
|
|
|
|
int
|
|
Game_getboardlettermultiplier(Game g,int r,int c)
|
|
{
|
|
return Board_getlettermultiplier(g->board,r,c);
|
|
}
|
|
|
|
/*********************************************************
|
|
*********************************************************/
|
|
|
|
int
|
|
Game_setrack_random(Game game, int check, set_rack_mode mode)
|
|
{
|
|
int i,c,v,min,nold;
|
|
tile_t l;
|
|
Bag b;
|
|
Playedrack p;
|
|
|
|
/* create a copy of the bag in which we can do everything we want */
|
|
b = Bag_create();
|
|
Bag_copy(b,game->bag);
|
|
|
|
if (Bag_ntiles(b) == 0)
|
|
return 1;
|
|
|
|
v = Bag_nvowels(b);
|
|
c = Bag_nconsonants(b);
|
|
min = 0;
|
|
|
|
if (check)
|
|
{
|
|
if (v == 0 || c == 0)
|
|
return 1;
|
|
if (Bag_nvowels(b) > 1 && Bag_nconsonants(b) > 1
|
|
&& Game_getnrounds(game) < 15)
|
|
min = 2;
|
|
else
|
|
min = 1;
|
|
}
|
|
|
|
p = game->playedracks[game->nrounds];
|
|
nold = Playedrack_nold(p);
|
|
|
|
/* "complement" with an empty rack is equivalent to ALL */
|
|
|
|
if (mode == RACK_ALL || nold == 0)
|
|
{
|
|
Playedrack_init(p);
|
|
for(i=0; (Bag_ntiles(b) != 0) && (i < RACK_MAX); i++)
|
|
{
|
|
l = Bag_select_random(b);
|
|
Bag_taketile(b,l);
|
|
Playedrack_addold(p,l);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tile_t ttmp[RACK_MAX];
|
|
int oldc = 0;
|
|
int oldv = 0;
|
|
|
|
/* we flush the "new" part of the rack */
|
|
|
|
Playedrack_resetnew(p);
|
|
|
|
/* "old" part must be taken into account in the bag */
|
|
|
|
Playedrack_getoldtiles(p,ttmp);
|
|
for(i=0; ttmp[i] ; i++)
|
|
{
|
|
if (Tiles_consonants[ttmp[i]])
|
|
oldc++;
|
|
if (Tiles_vowels[ttmp[i]])
|
|
oldv++;
|
|
Bag_taketile(b,ttmp[i]);
|
|
}
|
|
|
|
/* make sure we can complete the rack */
|
|
if (check)
|
|
{
|
|
/* RACK_MAX - nold is the number of letters to add */
|
|
if (min > oldc + RACK_MAX - nold ||
|
|
min > oldv + RACK_MAX - nold)
|
|
return 3;
|
|
}
|
|
|
|
/* take new tiles from the bag */
|
|
|
|
for(i=nold; (Bag_ntiles(b) != 0) && (i < RACK_MAX); i++)
|
|
{
|
|
l = Bag_select_random(b);
|
|
Bag_taketile(b,l);
|
|
Playedrack_addnew(p,l);
|
|
}
|
|
}
|
|
|
|
Bag_destroy(b);
|
|
|
|
if (check)
|
|
return Playedrack_check_rack(p, min) ? 0 : 2;
|
|
else
|
|
return 0; /* everything is ok */
|
|
}
|
|
|
|
static int
|
|
Game_rackinbag(Rack r, Bag b)
|
|
{
|
|
int i;
|
|
for(i=0; i < TILES_NUMBER; i++)
|
|
if (Rack_in(r,i) > Bag_in(b,i))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
int
|
|
Game_setrack_manual(Game game, int check, const char *t)
|
|
{
|
|
int i,min,ret;
|
|
tile_t l;
|
|
Playedrack p;
|
|
Rack rack;
|
|
|
|
p = game->playedracks[game->nrounds];
|
|
|
|
if (t == NULL || t[0] == 0)
|
|
{
|
|
Playedrack_init(p);
|
|
return 0;
|
|
}
|
|
|
|
Playedrack_init(p);
|
|
for(i=0; t[i] != 0 && t[i] != '+'; i++)
|
|
{
|
|
if ((l = chartocode(t[i]))==0)
|
|
{
|
|
return 1;
|
|
}
|
|
Playedrack_addold(p,l);
|
|
}
|
|
|
|
if (t[i] == '+')
|
|
{
|
|
for(i++; t[i] ; i++)
|
|
{
|
|
if ((l = chartocode(t[i])) == 0)
|
|
{
|
|
return 1;
|
|
}
|
|
Playedrack_addnew(p,l);
|
|
}
|
|
}
|
|
|
|
rack = Rack_create();
|
|
Playedrack_getrack(p,rack);
|
|
if ((ret = Game_rackinbag(rack,game->bag)) == 0)
|
|
{
|
|
Playedrack_init(p);
|
|
Rack_destroy(rack);
|
|
return 1;
|
|
}
|
|
Rack_destroy(rack);
|
|
|
|
if (check)
|
|
{
|
|
if (Bag_nvowels(game->bag) > 1 && Bag_nconsonants(game->bag) > 1
|
|
&& Game_getnrounds(game) < 15)
|
|
min = 2;
|
|
else
|
|
min = 1;
|
|
return Playedrack_check_rack(p, min) ? 0 : 2;
|
|
}
|
|
else
|
|
return 0; /* everything is ok */
|
|
}
|
|
|
|
/*********************************************************
|
|
*********************************************************/
|
|
|
|
|
|
int
|
|
Game_getcharinbag(Game g, char c)
|
|
{
|
|
return Bag_in(g->bag,chartocode(c));
|
|
}
|
|
|
|
|
|
/*********************************************************
|
|
*********************************************************/
|
|
|
|
|
|
int
|
|
Game_getnrounds(Game g)
|
|
{
|
|
return g->nrounds;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
Game_getplayedrack(Game g, int num, char buff[RACK_SIZE_MAX])
|
|
{
|
|
int i,l = 0;
|
|
Playedrack p;
|
|
tile_t bt[RACK_SIZE_MAX];
|
|
buff[0] = 0;
|
|
if (num < 0 || num > g->nrounds)
|
|
return;
|
|
p = g->playedracks[num];
|
|
Playedrack_getoldtiles(p, bt);
|
|
for(i=0; bt[i]; i++)
|
|
buff[l++] = codetochar(bt[i]);
|
|
Playedrack_getnewtiles(p, bt);
|
|
|
|
if (i && bt[0])
|
|
buff[l++] = '+';
|
|
|
|
for(i=0; bt[i]; i++)
|
|
buff[l++] = codetochar(bt[i]);
|
|
buff[l] = 0;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
Game_getplayedword(Game g, int num, char buff[WORD_SIZE_MAX])
|
|
{
|
|
Round r;
|
|
char c;
|
|
int i,l = 0;
|
|
buff[0] = 0;
|
|
if (num < 0 || num >= g->nrounds)
|
|
return;
|
|
r = g->playedrounds[num];
|
|
for(i=0; i < Round_wordlen(r); i++) {
|
|
c = codetochar(Round_gettile(r,i));
|
|
if (Round_joker(r,i))
|
|
c = tolower(c);
|
|
buff[l++] = c;
|
|
}
|
|
buff[i] = 0;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
Game_getplayedfirstcoord (Game g,int num,char buff[COOR_SIZE_MAX])
|
|
{
|
|
Round r;
|
|
buff[0] = 0;
|
|
if (num < 0 || num >= g->nrounds)
|
|
return;
|
|
r = g->playedrounds[num];
|
|
if (Round_dir(r) == HORIZONTAL)
|
|
sprintf(buff,"%c",Round_row(r) + 'A' - 1);
|
|
else
|
|
sprintf(buff,"%d",Round_column(r));
|
|
}
|
|
|
|
|
|
|
|
void
|
|
Game_getplayedsecondcoord(Game g,int num,char buff[COOR_SIZE_MAX])
|
|
{
|
|
Round r;
|
|
buff[0] = 0;
|
|
if (num < 0 || num >= g->nrounds)
|
|
return;
|
|
r = g->playedrounds[num];
|
|
if (Round_dir(r) == HORIZONTAL)
|
|
sprintf(buff,"%d",Round_column(r));
|
|
else
|
|
sprintf(buff,"%c",Round_row(r) + 'A' - 1);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
Game_getplayedcoord(Game g, int num, char buff[COOR_SIZE_MAX])
|
|
{
|
|
char c1[COOR_SIZE_MAX];
|
|
char c2[COOR_SIZE_MAX];
|
|
Game_getplayedfirstcoord(g,num,c1);
|
|
Game_getplayedsecondcoord(g,num,c2);
|
|
sprintf(buff,"%2s%2s",c1,c2);
|
|
}
|
|
|
|
|
|
|
|
int
|
|
Game_getplayedpoints(Game g, int num)
|
|
{
|
|
if (num < 0 || num >= g->nrounds)
|
|
return 0;
|
|
return Round_points(g->playedrounds[num]);
|
|
}
|
|
|
|
|
|
|
|
int
|
|
Game_getplayedbonus(Game g, int num)
|
|
{
|
|
if (num < 0 || num >= g->nrounds)
|
|
return 0;
|
|
return Round_bonus(g->playedrounds[num]);
|
|
}
|
|
|
|
/*********************************************************
|
|
*********************************************************/
|
|
|
|
int
|
|
Game_getnresults(Game g)
|
|
{
|
|
return Results_in(g->searchresults);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
Game_getsearchedword(Game g, int num ,char buff[WORD_SIZE_MAX])
|
|
{
|
|
Round r;
|
|
char c;
|
|
int i,l = 0;
|
|
buff[0] = 0;
|
|
if (num < 0 || num >= Results_in(g->searchresults))
|
|
return;
|
|
r = Results_get(g->searchresults,num);
|
|
for(i=0; i < Round_wordlen(r); i++) {
|
|
c = codetochar(Round_gettile(r,i));
|
|
if (Round_joker(r,i))
|
|
c = tolower(c);
|
|
buff[l++] = c;
|
|
}
|
|
buff[i] = 0;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
Game_getsearchedfirstcoord(Game g,int num,char buff[COOR_SIZE_MAX])
|
|
{
|
|
Round r;
|
|
buff[0] = 0;
|
|
if (num < 0 || num >= Results_in(g->searchresults))
|
|
return;
|
|
r = Results_get(g->searchresults,num);
|
|
if (Round_dir(r) == HORIZONTAL)
|
|
sprintf(buff,"%c",Round_row(r) + 'A' - 1);
|
|
else
|
|
sprintf(buff,"%d",Round_column(r));
|
|
}
|
|
|
|
|
|
|
|
void
|
|
Game_getsearchedsecondcoord(Game g, int num, char buff[COOR_SIZE_MAX])
|
|
{
|
|
Round r;
|
|
buff[0] = 0;
|
|
if (num < 0 || num >= Results_in(g->searchresults))
|
|
return;
|
|
r = Results_get(g->searchresults,num);
|
|
if (Round_dir(r) == HORIZONTAL)
|
|
sprintf(buff,"%d",Round_column(r));
|
|
else
|
|
sprintf(buff,"%c",Round_row(r) + 'A' - 1);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
Game_getsearchedcoord(Game g, int num, char buff[COOR_SIZE_MAX])
|
|
{
|
|
char c1[COOR_SIZE_MAX];
|
|
char c2[COOR_SIZE_MAX];
|
|
Game_getsearchedfirstcoord(g,num,c1);
|
|
Game_getsearchedsecondcoord(g,num,c2);
|
|
sprintf(buff,"%2s%2s",c1,c2);
|
|
}
|
|
|
|
|
|
|
|
int
|
|
Game_getsearchedpoints(Game g, int num)
|
|
{
|
|
if (num < 0 || num >= Results_in(g->searchresults))
|
|
return 0;
|
|
return Round_points(Results_get(g->searchresults,num));
|
|
}
|
|
|
|
|
|
|
|
int
|
|
Game_getsearchedbonus(Game g, int num)
|
|
{
|
|
if (num < 0 || num >= Results_in(g->searchresults))
|
|
return 0;
|
|
return Round_bonus(Results_get(g->searchresults,num));
|
|
}
|