eliot/game/board.c
Olivier Teulière b102aafbcd Support for 3 different game modes:
- 'Training' mode: same behaviour as before
  - 'Duplicate' mode: all the players (who can be human players or AI
    players) have the same rack, and the best word is played on the
    board
  - 'Free game' mode: the players (human or AI) have different racks,
    and they play one after another

Status:
  - Core: the main functions are written, but the API could be more
    homogeneous between the different modes.
  - Interfaces: the text interface is almost up-to-date, but the
    wxwindows one only supports Training mode.
  - AI: Currently, AI players always play the best word, which is
    optimal in Duplicate mode but not in FreeGame mode. Other strategies
    will be written in the future.
  - Handling of saved games is broken: a game can be saved and loaded,
    but no information about the mode and the players is stored, so it
    crashes whatever you do after loading the game.
2004-08-07 18:10:42 +00:00

462 lines
12 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: board.c,v 1.2 2004/08/07 18:10:42 ipkiss Exp $ */
#include <string.h>
#include <stdlib.h>
#include "dic.h"
#include "tiles.h"
#include "round.h"
#include "bag.h"
#include "rack.h"
#include "results.h"
#include "board.h"
#include "board_internals.h"
#define oo 0
#define __ 1
#define T2 2
#define T3 3
#define W2 2
#define W3 3
const int Board_tile_multipliers[BOARD_REALDIM][BOARD_REALDIM] =
{
{ oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo },
{ oo,__,__,__,T2,__,__,__,__,__,__,__,T2,__,__,__,oo },
{ oo,__,__,__,__,__,T3,__,__,__,T3,__,__,__,__,__,oo },
{ oo,__,__,__,__,__,__,T2,__,T2,__,__,__,__,__,__,oo },
{ oo,T2,__,__,__,__,__,__,T2,__,__,__,__,__,__,T2,oo },
{ oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
{ oo,__,T3,__,__,__,T3,__,__,__,T3,__,__,__,T3,__,oo },
{ oo,__,__,T2,__,__,__,T2,__,T2,__,__,__,T2,__,__,oo },
{ oo,__,__,__,T2,__,__,__,__,__,__,__,T2,__,__,__,oo },
{ oo,__,__,T2,__,__,__,T2,__,T2,__,__,__,T2,__,__,oo },
{ oo,__,T3,__,__,__,T3,__,__,__,T3,__,__,__,T3,__,oo },
{ oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
{ oo,T2,__,__,__,__,__,__,T2,__,__,__,__,__,__,T2,oo },
{ oo,__,__,__,__,__,__,T2,__,T2,__,__,__,__,__,__,oo },
{ oo,__,__,__,__,__,T3,__,__,__,T3,__,__,__,__,__,oo },
{ oo,__,__,__,T2,__,__,__,__,__,__,__,T2,__,__,__,oo },
{ oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo }
};
const int Board_word_multipliers[BOARD_REALDIM][BOARD_REALDIM] =
{
{ oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo },
{ oo,W3,__,__,__,__,__,__,W3,__,__,__,__,__,__,W3,oo },
{ oo,__,W2,__,__,__,__,__,__,__,__,__,__,__,W2,__,oo },
{ oo,__,__,W2,__,__,__,__,__,__,__,__,__,W2,__,__,oo },
{ oo,__,__,__,W2,__,__,__,__,__,__,__,W2,__,__,__,oo },
{ oo,__,__,__,__,W2,__,__,__,__,__,W2,__,__,__,__,oo },
{ oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
{ oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
{ oo,W3,__,__,__,__,__,__,W2,__,__,__,__,__,__,W3,oo },
{ oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
{ oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
{ oo,__,__,__,__,W2,__,__,__,__,__,W2,__,__,__,__,oo },
{ oo,__,__,__,W2,__,__,__,__,__,__,__,W2,__,__,__,oo },
{ oo,__,__,W2,__,__,__,__,__,__,__,__,__,W2,__,__,oo },
{ oo,__,W2,__,__,__,__,__,__,__,__,__,__,__,W2,__,oo },
{ oo,W3,__,__,__,__,__,__,W3,__,__,__,__,__,__,W3,oo },
{ oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo }
};
Board
Board_create(void)
{
Board b;
b = (Board) malloc(sizeof(struct tboard));
Board_init(b);
return b;
}
void
Board_init(Board b)
{
int i, j;
memset(b, 0, sizeof(struct tboard));
for (i = 1; i <= BOARD_DIM; i++)
for (j = 1; j <= BOARD_DIM; j++)
{
b->joker_r[i][j] = 0;
b->joker_c[i][j] = 0;
b->cross_r[i][j] = CROSS_MASK;
b->cross_c[i][j] = CROSS_MASK;
b->point_r[i][j] = -1;
b->point_c[i][j] = -1;
}
}
void
Board_destroy(Board b)
{
if (b)
free(b);
}
tile_t
Board_tile(Board b, int row, int column)
{
return b->tiles_r[row][column];
}
int
Board_joker(Board b, int row, int column)
{
return b->joker_r[row][column];
}
int
Board_vacant(Board b, int row, int column)
{
if (row < 1 || row > BOARD_DIM ||
column < 1 || column > BOARD_DIM)
return 0;
return b->tiles_r[row][column] == 0;
}
void
Board_addround(Dictionary d, Board b, Round r)
{
tile_t t;
int row, column, i;
row = Round_row(r);
column = Round_column(r);
if (Round_dir(r) == HORIZONTAL)
{
for (i = 0; i < Round_wordlen(r); i++)
if (!b->tiles_r[row][column + i])
{
t = Round_gettile(r,i);
b->tiles_r[row][column + i] = t;
b->joker_r[row][column + i] = Round_joker(r,i);
b->tiles_c[column + i][row] = t;
b->joker_c[column + i][row] = Round_joker(r,i);
}
}
else
{
for (i = 0; i < Round_wordlen(r); i++)
if (!b->tiles_r[row + i][column])
{
t = Round_gettile(r,i);
b->tiles_r[row + i][column] = t;
b->joker_r[row + i][column] = Round_joker(r,i);
b->tiles_c[column][row + i] = t;
b->joker_c[column][row + i] = Round_joker(r,i);
}
}
Board_buildcross(d, b);
#ifdef DEBUG
Board_check_double(b);
#endif
}
void
Board_removeround(Dictionary d, Board b, Round r)
{
int row, column, i;
row = Round_row(r);
column = Round_column(r);
if (Round_dir(r) == HORIZONTAL)
{
for (i = 0; i < Round_wordlen(r); i++)
if (Round_playedfromrack(r,i))
{
b->tiles_r[row][column + i] = 0;
b->joker_r[row][column + i] = 0;
b->tiles_c[column + i][row] = 0;
b->joker_c[column + i][row] = 0;
}
}
else
{
for (i = 0; i < Round_wordlen(r); i++)
if (Round_playedfromrack(r,i))
{
b->tiles_r[row + i][column] = 0;
b->joker_r[row + i][column] = 0;
b->tiles_c[column][row + i] = 0;
b->joker_c[column][row + i] = 0;
}
}
Board_buildcross(d, b);
#ifdef DEBUG
Board_check_double(b);
#endif
}
/* XXX: There is duplicated code with board_search.c.
* We could probably factorize something... */
int
Board_checkround_aux(
tile_t tiles[BOARD_REALDIM][BOARD_REALDIM],
unsigned int cross[BOARD_REALDIM][BOARD_REALDIM],
int points[BOARD_REALDIM][BOARD_REALDIM],
char joker[BOARD_REALDIM][BOARD_REALDIM],
Round round,
int firstturn)
{
tile_t t;
int row, col, i;
int l, p, fromrack, isolated;
int pts, ptscross, wordmul;
isolated = 1;
fromrack = 0;
pts = 0;
ptscross = 0;
wordmul = 1;
row = Round_row(round);
col = Round_column(round);
/* Is the word an extension of another word? */
if (tiles[row][col - 1] || tiles[row][col + Round_wordlen(round)])
return 1;
for (i = 0; i < Round_wordlen(round); i++)
{
t = Round_gettile(round, i);
if (tiles[row][col + i])
{
/* There is already a letter on the board */
if (tiles[row][col + i] != t)
return 2;
isolated = 0;
Round_setfromboard(round, i);
if (! joker[row][col + i])
pts += Tiles_points[t];
}
else
{
/* The letter is not yet on the board */
if (cross[row][col + i] & (1 << t))
{
/* A non-trivial cross-check means an anchor square */
if (cross[row][col + i] != CROSS_MASK)
isolated = 0;
if (! Round_joker(round, i))
l = Tiles_points[t] *
Board_tile_multipliers[row][col + i];
else
l = 0;
pts += l;
wordmul *= Board_word_multipliers[row][col + i];
p = points[row][col + i];
if (p >= 0)
{
ptscross += (p + l) *
Board_word_multipliers[row][col + i];
}
fromrack++;
Round_setfromrack(round, i);
}
else
{
/* The letter is not in the crosscheck */
return 3;
}
}
}
/* There must be at least 1 letter from the rack */
if (fromrack == 0)
return 4;
/* The word must cover at least one anchor square, except
* for the first turn */
if (isolated && ! firstturn)
return 5;
/* The first word must be horizontal */
if (firstturn && Round_dir(round) == VERTICAL)
return 6;
/* The first word must cover the H8 square */
if (firstturn
&& (row != 8 || col > 8 || col + Round_wordlen(round) <= 8))
{
return 7;
}
/* Set the points and bonus */
pts = ptscross + pts * wordmul + 50 * (fromrack == 7);
Round_setpoints(round, pts);
Round_setbonus(round, fromrack == 7);
return 0;
}
int
Board_checkround(Dictionary dic, Board board, Round round, int firstturn)
{
if (Round_dir(round) == HORIZONTAL)
return Board_checkround_aux(board->tiles_r, board->cross_r,
board->point_r, board->joker_r,
round, firstturn);
else
{
int res, tmp;
// XXX: ugly!
/* Exchange the coordinates temporarily */
tmp = Round_row(round);
Round_setrow(round, Round_column(round));
Round_setcolumn(round, tmp);
res = Board_checkround_aux(board->tiles_c, board->cross_c,
board->point_c, board->joker_c,
round, firstturn);
/* Restore the coordinates */
tmp = Round_row(round);
Round_setrow(round, Round_column(round));
Round_setcolumn(round, tmp);
return res;
}
}
void
Board_testround(Board b, Round r)
{
tile_t t;
int row, column, i;
row = Round_row(r);
column = Round_column(r);
if (Round_dir(r) == HORIZONTAL)
{
for (i = 0; i < Round_wordlen(r); i++)
if (!b->tiles_r[row][column + i])
{
t = Round_gettile(r,i);
b->tiles_r[row][column + i] = t;
b->joker_r[row][column + i] = Round_joker(r,i);
b->tests_r[row][column + i] = 1;
b->tiles_c[column + i][row] = t;
b->joker_c[column + i][row] = Round_joker(r,i);
}
}
else
{
for (i = 0; i < Round_wordlen(r); i++)
if (!b->tiles_r[row + i][column])
{
t = Round_gettile(r,i);
b->tiles_r[row + i][column] = t;
b->joker_r[row + i][column] = Round_joker(r,i);
b->tests_r[row + i][column] = 1;
b->tiles_c[column][row + i] = t;
b->joker_c[column][row + i] = Round_joker(r,i);
}
}
}
void
Board_removetestround(Board b)
{
int i,j;
for(i=1; i <= BOARD_DIM; i++)
for(j=1; j <= BOARD_DIM; j++)
{
if (b->tests_r[i][j])
{
b->tiles_r[i][j] = 0;
b->tests_r[i][j] = 0;
b->joker_r[i][j] = 0;
b->tiles_c[j][i] = 0;
b->joker_c[j][i] = 0;
}
}
}
char
Board_gettestchar(Board b,int r,int c)
{
return b->tests_r[r][c];
}
int
Board_getwordmultiplier(Board b,int r,int c)
{
b++; /* unused */
if (r < BOARD_MIN || r > BOARD_MAX ||
c < BOARD_MIN || c > BOARD_MAX)
return 0;
return Board_word_multipliers[r][c];
}
int
Board_getlettermultiplier(Board b, int r, int c)
{
b++; /* unused */
if (r < BOARD_MIN || r > BOARD_MAX ||
c < BOARD_MIN || c > BOARD_MAX)
return 0;
return Board_tile_multipliers[r][c];
}
#ifdef DEBUG
void
Board_check_double(Board b)
{
int r,c;
for(r=BOARD_MIN; r <= BOARD_MAX; r++)
for(c=BOARD_MIN; c <= BOARD_MAX; c++)
{
if (b->tiles_r[r][c] != b->tiles_c[c][r])
printf("tiles diff %d %d\n",r,c);
if (b->cross_r[r][c] != b->cross_c[c][r])
printf("cross diff %d %d\n",r,c);
if (b->point_r[r][c] != b->point_c[c][r])
printf("point diff %d %d\n",r,c);
if (b->joker_r[r][c] != b->joker_c[c][r])
printf("joker diff %d %d\n",r,c);
}
printf("**\n");
}
#endif