You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegames/kbackgammon/kbgstatus.cpp

545 lines
11 KiB

/* Yo Emacs, this -*- C++ -*-
Copyright (C) 2001 Jens Hoefkens
jens@hoefkens.com
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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kbgstatus.h"
#include "kbgstatus.moc"
#include <stdlib.h>
#include <stdio.h>
/*
* Parse a rawboard description from FIBS and initialize members.
*/
KBgStatus::KBgStatus(const QString &rawboard)
{
/*
* This is the format string from hell...
*/
const char *format = ("%*[^:]%*[:]%99[^:]%*[:]%99[^:]%*[:]%i%*[:]%i%*[:]%i%*[:]"
"%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]"
"%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]"
"%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]"
"%i%*[:]"
"%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]"
"%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]"
"%i%*[:]%i%*[:]");
char opponent[100], player[100];
QString cap;
int board[26], ldice[2][2], maydouble[2], scratch[4], onhome[2], onbar[2];
int points[2];
int tomove, lturn, color, cube, direction, redoubles, bar, home, length;
// split the incoming line at colons - latin1() is fine, since the string comes from FIBS.
sscanf (rawboard.latin1(), format,
player, opponent,
&length,
&points[0], &points[1],
&board[ 0], &board[ 1], &board[ 2], &board[ 3], &board[ 4], &board[ 5],
&board[ 6], &board[ 7], &board[ 8], &board[ 9], &board[10], &board[11],
&board[12], &board[13], &board[14], &board[15], &board[16], &board[17],
&board[18], &board[19], &board[20], &board[21], &board[22], &board[23],
&board[24], &board[25],
&lturn,
&ldice[US ][0], &ldice[US ][1], &ldice[THEM][0], &ldice[THEM][1],
&cube,
&maydouble[US], &maydouble[THEM],
&doubled_,
&color,
&direction,
&home, &bar,
&onhome[US], &onhome[THEM], // on home
&onbar[US], &onbar[THEM], // on bar
&tomove,
&scratch[2], &scratch[3], // forced move & did crawford
&redoubles);
player_[US] = player;
player_[THEM] = opponent;
setCube(cube, maydouble[US], maydouble[THEM]);
setDirection(direction);
setColor(color);
for (int i = 1; i < 25; i++) {
if (board[i] == 0 || color == board[i]/abs(board[i]))
setBoard(i, US, abs(board[i]));
else
setBoard(i, THEM, abs(board[i]));
}
setDice(US , 0, ldice[US ][0]);
setDice(US , 1, ldice[US ][1]);
setDice(THEM, 0, ldice[THEM][0]);
setDice(THEM, 1, ldice[THEM][1]);
setHome(US, onhome[US ]);
setHome(THEM, onhome[THEM]);
setBar(US, board[ bar]);
setBar(THEM, board[25-bar]);
setPoints(US, points[0]);
setPoints(THEM, points[1]);
if (lturn == 0)
setLength(-1);
else
setLength(length);
int t = lturn*color;
if (t > 0) setTurn(US);
if (t < 0) setTurn(THEM);
if (t == 0) setTurn(NONE);
}
/*
* Constructor initializes the status to an empty board with cube one
* and empty dice.
*/
KBgStatus::KBgStatus()
: QObject()
{
/*
* Initialize members
*/
for (int i = 0; i < 26; ++i)
setBoard(i, US, 0);
for (int i = US; i <= THEM; i++) {
setDice (i, 0, 0);
setDice (i, 1, 0);
setHome (i, 0);
setBar (i, 0);
setPoints(i, -1);
setPlayer(i, QString::null);
}
setColor(White, US);
setCube(1, BOTH); // also initializes maydouble
setDirection(1);
setLength(-1);
setTurn(NONE);
// initialize members without assignment functions
doubled_ = 0;
}
/*
* Copy constructor calls private utility function.
*/
KBgStatus::KBgStatus(const KBgStatus &rhs)
: QObject()
{
copy(rhs);
}
/*
* Destructor
*/
KBgStatus::~KBgStatus()
{
// nothing to do
}
/*
* Assignment operator shares a lot of code with the copy
* constructor.
*/
KBgStatus& KBgStatus::operator=(const KBgStatus &rhs)
{
if (this == &rhs) return *this;
copy(rhs);
return *this;
}
void KBgStatus::copy(const KBgStatus &rhs)
{
for (int i = 0; i < 26; i++)
board_[i] = rhs.board_[i];
for (int i = US; i <= THEM; i++) {
home_[i] = rhs.home_[i];
bar_ [i] = rhs.bar_ [i];
dice_[i][0] = rhs.dice_[i][0];
dice_[i][1] = rhs.dice_[i][1];
maydouble_[i] = rhs.maydouble_[i];
player_ [i] = rhs.player_ [i];
points_ [i] = rhs.points_ [i];
}
cube_ = rhs.cube_;
direction_ = rhs.direction_;
color_ = rhs.color_;
turn_ = rhs.turn_;
doubled_ = rhs.doubled_;
}
/*
* Access functions
*/
int KBgStatus::board(const int &i) const
{
return ((0 < i && i < 25) ? color_*board_[i] : 0);
}
int KBgStatus::home(const int &w) const
{
return ((w == US || w == THEM) ? color_*home_[w] : 0);
}
int KBgStatus::bar(const int &w) const
{
return ((w == US || w == THEM) ? color_*bar_[w] : 0);
}
int KBgStatus::color(const int &w) const
{
return ((w == THEM) ? -color_ : color_);
}
int KBgStatus::direction() const
{
return direction_;
}
int KBgStatus::dice(const int &w, const int &n) const
{
if ((w == US || w == THEM) && (n == 0 || n == 1))
return dice_[w][n];
else
return 0;
}
int KBgStatus::cube(const int &w) const
{
if (w == US || w == THEM)
return ((maydouble_[w]) ? cube_ : -cube_);
return 0;
}
int KBgStatus::points(const int& w) const
{
return ((w == US || w == THEM) ? points_[w] : -1);
}
QString KBgStatus::player(const int &w) const
{
return ((w == US || w == THEM) ? player_[w] : QString::null);
}
int KBgStatus::length() const
{
return length_;
}
int KBgStatus::turn() const
{
return turn_;
}
bool KBgStatus::doubled() const
{
return doubled_;
}
/*
* Assignment functions
*/
void KBgStatus::setBoard(const int &i, const int &w, const int &v)
{
if (0 < i && i < 25) {
if (w == US)
board_[i] = abs(v);
else if (w == THEM)
board_[i] = -abs(v);
}
}
void KBgStatus::setHome(const int &w, const int &v)
{
if (w == US)
home_[w] = abs(v);
else if (w == THEM)
home_[w] = -abs(v);
}
void KBgStatus::setBar(const int& w, const int& v)
{
if (w == US)
bar_[w] = abs(v);
else if (w == THEM)
bar_[w] = -abs(v);
}
void KBgStatus::setColor(const int &c, const int &w)
{
if (w == US)
color_ = ((c < 0) ? Black : White);
else if (w == THEM)
color_ = ((c < 0) ? White : Black);
}
void KBgStatus::setDirection(const int &dir)
{
direction_ = ((dir < 0) ? -1 : +1);
}
void KBgStatus::setDice(const int &w, const int &n, const int &v)
{
if ((w == US || w == THEM) && (n == 0 || n == 1)) {
if (0 <= v && v <= 6)
dice_[w][n] = v;
else
dice_[w][n] = 0;
}
}
void KBgStatus::setCube(const int &c, const bool &us, const bool &them)
{
int w = NONE;
if (us) w = US;
if (them) w = THEM;
if (us && them) w = BOTH;
setCube(c, w);
}
void KBgStatus::setCube(const int &c, const int &w)
{
// assume that int has at least 32 bits...
for (int i = 0; i < 31; i++) {
if (1<<i == (cube_ = c)) break;
cube_ = 0;
}
maydouble_[US ] = (w == US || w == BOTH);
maydouble_[THEM] = (w == THEM || w == BOTH);
}
void KBgStatus::setPoints(const int &w, const int &p)
{
if (w == US || w == THEM)
points_[w] = p;
}
void KBgStatus::setPlayer(const int &w, const QString &name)
{
if (w == US || w == THEM)
player_[w] = name;
}
void KBgStatus::setLength(const int &l)
{
length_ = l;
}
void KBgStatus::setTurn(const int &w)
{
if (w == US || w == THEM || w == BOTH)
turn_ = w;
}
/*
* Utility functions
*/
int KBgStatus::moves() const
{
int start, dir;
/*
* Return an error if it isn't anybodies turn.
*/
if ((turn() != US) && (turn() != THEM))
return -1;
/*
* Determine tha direction of the current move
*/
if ((turn() == US && direction() < 0) || (turn() == THEM && direction() > 0)) {
start = 25;
dir = -1;
} else {
start = 0;
dir = 1;
}
/*
* Get the current dice transferred into the move[] array. The
* final zero is a marker
*/
int move[5] = {0, 0, 0, 0, 0};
move[0] = dice(turn(), 0);
move[1] = dice(turn(), 1);
if (move[0] == move[1]) {
move[3] = move[2] = move[0];
// saves some work further down
if (move[0] == 0)
return 0;
}
bool doubledice = (move[3] != 0);
int count = 4;
/*
* Get a copy of ourselves. That way we can mess around with
* the internals of the game.
*/
KBgStatus sc(*this);
/*
* Start with getting all checkers off the bar
*/
while (count > 0 && sc.bar(turn()) != 0) {
if (move[--count] != 0) {
if (color(turn())*sc.board(start+dir*move[count]) >= -1) {
sc.setBar(turn(), abs(sc.bar(turn()))-1);
sc.setBoard(start + dir*move[count], turn(), 1);
move[count] = 0;
}
}
}
/*
* Collect remaining moves in the beginning of the move array
*/
int j = 0;
for (int i = 0; i < 4; i++) {
if ((move[j] = move[i]))
++j;
if (i > j) move[i] = 0;
}
/*
* Find number of remaining moves
*/
int moves = 0;
move[4] = 0;
while (move[moves])
++moves;
/*
* Done or no more moves because the bar is not empty
*/
if (sc.bar(turn()) != 0 || move[0] == 0)
return (move[3] ? 4 - moves : 2 - moves);
/*
* Try to find possible moves on the board
*/
if (moves == 1 || move[0] == move[1]) {
/*
* Order doesn't matter, all moves are equal
*/
while (--moves >= 0 && movePossible(sc, move[moves], start, dir));
moves++;
return (doubledice ? 4 - moves : 2 - moves);
} else {
/*
* Order does matter; try both ways.
*/
moves = 0;
for (int i = 0; i < 25; i++) {
if (movePossible(sc, move[0], start + i*dir, dir)) {
moves = 1;
if (movePossible(sc, move[1], start, dir)) {
return 2;
}
}
// Restore scratch copy...
sc = *this;
}
for (int i = 0; i < 25; i++) {
if (movePossible(sc, move[1], start + i*dir, dir)) {
moves = 1;
if (movePossible(sc, move[0], start, dir)) {
return 2;
}
}
// Restore scratch copy...
sc = *this;
}
return moves;
}
}
bool KBgStatus::movePossible(KBgStatus &sc, int a, int start, int dir) const
{
/*
* Determine where the first checker in moving direction is
* located
*/
int first = (dir > 0) ? 1 : 24;
int last = (dir > 0) ? 25 : 0;
while (first != last && color(turn())*sc.board(first) <= 0)
first += dir;
/*
* Are we moving off ?
*/
bool off = false;
if ((dir > 0 && first > 18) || (dir < 0 && first < 7))
off = true;
/*
* Find a move by exhaustive search.
*/
while (true) {
start += dir;
int final = start+dir*a;
/*
* Make absolutely sure that the loop terminates eventually
*/
if (start <= 0 || start >= 25)
return false;
if (color(turn())*sc.board(start) > 0) {
if (0 < final && final < 25 && color(turn())*sc.board(final) >= -1) {
sc.setBoard(start, turn(), abs(sc.board(start)) - 1);
sc.setBoard(final, turn(), abs(sc.board(final)) + 1);
return true;
} else if (off && (final == 0 || final == 25)) {
sc.setBoard(start, turn(), abs(sc.board(start)) - 1);
sc.setHome(turn(), abs(sc.home(turn())) + 1);
return true;
} else if (off && first == start && (final > 24 || final < 1)) {
sc.setBoard(start, turn(), abs(sc.board(start)) - 1);
sc.setHome(turn(), abs(sc.home(turn())) + 1);
return true;
}
}
}
}
// EOF