#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <Carbon/Carbon.h>
#include <CoreFoundation/CoreFoundation.h>
#include "ChessListener.h"
/*
* Copied from gnuglue.h
*/
/* players */
enum {
WHITE = 0,
BLACK,
NEUTRAL
};
/* pieces */
enum {
NO_PIECE = 0,
PAWN,
KNIGHT,
BISHOP,
ROOK,
QUEEN,
KING
};
struct CLCoord {
explicit CLCoord(int coord) : fCoord(coord), fCol(coord & 7), fRow(coord >> 3) {}
explicit CLCoord(int column, int row) : fCol(column), fRow(row)
{ fCoord = (fRow|fCol) & 0xFFFFFFF8 ? -1 : (fRow << 3) | column; }
explicit CLCoord(const char * name) : fCol(name[0]-'a'), fRow(name[1]-'1')
{ fCoord = (fRow|fCol) & 0xFFFFFFF8 ? -1 : (fRow << 3) | fCol; }
int fCoord;
int fCol;
int fRow;
char ColLetter() const { return 'a'+fCol; }
char RowLetter() const { return '1'+fRow; }
};
class CLMoveBuilder {
public:
virtual void StartMoveList();
virtual void Move(int piece, const CLCoord & fromCoord, const CLCoord & toCoord, bool take, bool omitFrom) = 0;
virtual void EndMoveList();
virtual ~CLMoveBuilder();
};
void CLMoveBuilder::StartMoveList()
{
}
void CLMoveBuilder::EndMoveList()
{
}
CLMoveBuilder::~CLMoveBuilder()
{
}
class CLDebugMoveBuilder : public CLMoveBuilder {
public:
CLDebugMoveBuilder(CLMoveBuilder * nextBuilder) : fNextBuilder(nextBuilder) {}
virtual void StartMoveList();
virtual void Move(int piece, const CLCoord & fromCoord, const CLCoord & toCoord, bool take, bool omitFrom);
virtual void EndMoveList();
private:
CLMoveBuilder * fNextBuilder;
};
void CLDebugMoveBuilder::Move(int piece, const CLCoord & fromCoord, const CLCoord & toCoord, bool take, bool omitFrom)
{
static char * pieceName[] = {"", "Pawn", "Knight", "Bishop", "Rook", "Queen", "King"};
if (omitFrom)
return;
if (piece == KING && fromCoord.fCol==4 && !(toCoord.fCol&1) && toCoord.fRow==fromCoord.fRow)
if (toCoord.fCol==2)
fprintf(stderr, "castle%s\n", omitFrom ? "" : " queen side");
else
fprintf(stderr, "castle%s\n", omitFrom ? "" : " king side");
else if (!omitFrom)
fprintf(stderr, "[%s] %c%c %s %c%c\n", pieceName[piece],
fromCoord.ColLetter(), fromCoord.RowLetter(),
(take ? "( takes | to )" : "to"),
toCoord.ColLetter(), toCoord.RowLetter());
else
fprintf(stderr, "%s %s %c%c\n", pieceName[piece],
(take ? "( takes | to )" : "to"),
toCoord.ColLetter(), toCoord.RowLetter());
if (fNextBuilder)
fNextBuilder->Move(piece, fromCoord, toCoord, take, omitFrom);
}
void CLDebugMoveBuilder::StartMoveList()
{
fprintf(stderr, "----- Legal moves:\n");
if (fNextBuilder)
fNextBuilder->StartMoveList();
}
void CLDebugMoveBuilder::EndMoveList()
{
fprintf(stderr, "-----\n");
if (fNextBuilder)
fNextBuilder->EndMoveList();
}
class CLSRMoveBuilder : public CLMoveBuilder {
public:
CLSRMoveBuilder();
~CLSRMoveBuilder();
virtual void StartMoveList();
virtual void Move(int piece, const CLCoord & fromCoord, const CLCoord & toCoord, bool take, bool omitFrom);
virtual void EndMoveList();
bool OK() { return fOK; }
SRRecognitionSystem RecSystem() { return fRecSystem; }
SRRecognizer Recognizer() { return fRecognizer; }
void StartListening();
void StopListening();
private:
SRRecognitionSystem fRecSystem;
SRRecognizer fRecognizer;
SRLanguageModel fModel;
SRLanguageModel fTakesModel;
SRWord fToModel;
SRWord fPieceModel[7];
SRWord fCastleModel;
SRPhrase fUndoModel;
SRPhrase fKingSideModel;
SRPhrase fQueenSideModel;
SRWord fColModel[8];
SRWord fRowModel[8];
bool fOK;
bool fListening;
void MakeWord(SRWord * word, const char * text, bool optional);
void MakePhrase(SRPhrase * phrase, const char * text, bool optional);
void MakeAlt(SRLanguageModel * alt, const char * t1, const char * t2, bool optional);
void MakeHelp();
};
CLSRMoveBuilder::CLSRMoveBuilder()
{
const char * SvA = getenv("CHESS_SPEED");
unsigned short speed= SvA ? atoi(SvA) : 25;
long refCon = -1;
fOK = false;
fListening = false;
if (SROpenRecognitionSystem (&fRecSystem, kSRDefaultRecognitionSystemID))
goto failRecSystem;
if (SRNewRecognizer(fRecSystem, &fRecognizer, kSRDefaultSpeechSource))
goto failRecognizer;
SRSetProperty(fRecognizer, kSRSpeedVsAccuracyParam, &speed, sizeof(speed));
SRNewLanguageModel(fRecSystem, &fModel, "<moves>", 7);
MakeWord(&fToModel, "to", false);
MakeWord(fPieceModel+1, "pawn", false);
MakeWord(fPieceModel+2, "knight", false);
MakeWord(fPieceModel+3, "bishop", false);
MakeWord(fPieceModel+4, "rook", false);
MakeWord(fPieceModel+5, "queen", false);
MakeWord(fPieceModel+6, "king", false);
MakeWord(&fCastleModel, "castle", false);
MakePhrase(&fKingSideModel, "king side", false);
MakePhrase(&fQueenSideModel, "queen side", false);
MakePhrase(&fUndoModel, "take back move", false);
SRSetProperty(fUndoModel, kSRRefCon, &refCon, sizeof(refCon));
MakeWord(fColModel+0, "a", false);
MakeWord(fColModel+1, "b", false);
MakeWord(fColModel+2, "c", false);
MakeWord(fColModel+3, "d", false);
MakeWord(fColModel+4, "e", false);
MakeWord(fColModel+5, "f", false);
MakeWord(fColModel+6, "g", false);
MakeWord(fColModel+7, "h", false);
MakeWord(fRowModel+0, "1", false);
MakeWord(fRowModel+1, "2", false);
MakeWord(fRowModel+2, "3", false);
MakeWord(fRowModel+3, "4", false);
MakeWord(fRowModel+4, "5", false);
MakeWord(fRowModel+5, "6", false);
MakeWord(fRowModel+6, "7", false);
MakeWord(fRowModel+7, "8", false);
MakeAlt(&fTakesModel, "takes", "to", false);
MakeHelp();
fOK = true;
return;
failRecognizer:
SRCloseRecognitionSystem(fRecSystem);
failRecSystem:
;
}
CLSRMoveBuilder::~CLSRMoveBuilder()
{
if (!fOK)
return;
if (fListening)
SRStopListening(fRecognizer);
SRReleaseObject(fModel);
SRReleaseObject(fTakesModel);
SRReleaseObject(fToModel);
for (int piece = 1; piece<7; ++piece) {
SRReleaseObject(fPieceModel[piece]);
}
for (int rowcol = 0; rowcol<8; ++rowcol) {
SRReleaseObject(fRowModel[rowcol]);
SRReleaseObject(fColModel[rowcol]);
}
SRReleaseObject(fCastleModel);
SRReleaseObject(fKingSideModel);
SRReleaseObject(fQueenSideModel);
SRReleaseObject(fUndoModel);
SRReleaseObject(fRecognizer);
SRCloseRecognitionSystem(fRecSystem);
}
void CLSRMoveBuilder::StopListening()
{
if (fListening)
SRStopListening(fRecognizer);
fListening = false;
}
void CLSRMoveBuilder::StartMoveList()
{
StopListening();
SREmptyLanguageObject(fModel);
SRAddLanguageObject(fModel, fUndoModel);
}
void CLSRMoveBuilder::Move(int piece, const CLCoord & fromCoord, const CLCoord & toCoord, bool take, bool omitFrom)
{
if (omitFrom)
return;
SRPath path;
SRNewPath(fRecSystem, &path);
OSType refCon =
(fromCoord.ColLetter() << 24)
| (fromCoord.RowLetter() << 16)
| ( toCoord.ColLetter() << 8)
| toCoord.RowLetter();
SRSetProperty (path, kSRRefCon, &refCon, sizeof(refCon));
if (piece == KING && fromCoord.fCol==4 && !(toCoord.fCol&1) && toCoord.fRow==fromCoord.fRow) { // Castle
SRAddLanguageObject(path, fCastleModel);
if (!omitFrom)
SRAddLanguageObject(path, toCoord.fCol==6 ? fKingSideModel : fQueenSideModel);
} else {
if (omitFrom) {
SRAddLanguageObject(path, fPieceModel[piece]);
} else {
SRAddLanguageObject(path, fPieceModel[piece]);
SRAddLanguageObject(path, fColModel[fromCoord.fCol]);
SRAddLanguageObject(path, fRowModel[fromCoord.fRow]);
}
SRAddLanguageObject(path, take ? fTakesModel : fToModel);
SRAddLanguageObject(path, fColModel[toCoord.fCol]);
SRAddLanguageObject(path, fRowModel[toCoord.fRow]);
}
SRAddLanguageObject(fModel, path);
}
void CLSRMoveBuilder::StartListening()
{
if (!fListening)
SRStartListening(fRecognizer);
fListening = true;
}
void CLSRMoveBuilder::EndMoveList()
{
SRSetLanguageModel(fRecognizer, fModel);
StartListening();
}
void CLSRMoveBuilder::MakeWord(SRWord * word, const char * text, bool optional)
{
SRNewWord(fRecSystem, word, text, strlen(text));
if (optional) {
Boolean opt = true;
SRSetProperty (*word, kSROptional, &opt, sizeof(Boolean));
}
}
void CLSRMoveBuilder::MakePhrase(SRPhrase * phrase, const char * text, bool optional)
{
SRNewPhrase(fRecSystem, phrase, text, strlen(text));
if (optional) {
Boolean opt = true;
SRSetProperty (*phrase, kSROptional, &opt, sizeof(Boolean));
}
}
void CLSRMoveBuilder::MakeAlt(SRLanguageModel * alt, const char * t1, const char * t2, bool optional)
{
SRNewLanguageModel(fRecSystem, alt, "", 0);
SRWord w;
SRNewWord(fRecSystem, &w, t1, strlen(t1));
SRAddLanguageObject(*alt, w);
SRReleaseObject(w);
SRNewWord(fRecSystem, &w, t2, strlen(t2));
SRAddLanguageObject(*alt, w);
SRReleaseObject(w);
if (optional) {
Boolean opt = true;
SRSetProperty (*alt, kSROptional, &opt, sizeof(Boolean));
}
}
static CFDataRef sHelpData;
void CL_SetHelp(unsigned len, const void * data)
{
sHelpData = CFDataCreate(NULL, (const UInt8 *)data, len);
}
void CLSRMoveBuilder::MakeHelp()
{
CFMutableDictionaryRef dict =
(CFMutableDictionaryRef)
CFPropertyListCreateFromXMLData(NULL, sHelpData, kCFPropertyListMutableContainers, NULL);
ProcessSerialNumber psn;
GetCurrentProcess(&psn);
CFNumberRef num = CFNumberCreate(NULL, kCFNumberSInt32Type, &psn.highLongOfPSN);
CFDictionaryAddValue(dict, CFSTR("ProcessPSNHigh"), num);
CFRelease(num);
num = CFNumberCreate(NULL, kCFNumberSInt32Type, &psn.lowLongOfPSN);
CFDictionaryAddValue(dict, CFSTR("ProcessPSNLow"), num);
CFRelease(num);
CFDataRef finalData = CFPropertyListCreateXMLData(NULL, dict);
if (finalData) {
SRSetProperty(fRecognizer, 'cdpl', CFDataGetBytePtr(finalData), CFDataGetLength(finalData));
CFRelease(finalData);
}
CFRelease(dict);
}
class CLMoveGenerator {
public:
CLMoveGenerator(CLMoveBuilder * builder) : fBuilder(builder) {}
void Generate(int color, short pieces[], short colors[]);
private:
bool TryMove(int piece, const CLCoord & from, const CLCoord & to);
bool TryMove(int piece, const CLCoord & from, int dCol, int dRow)
{ return TryMove(piece, from, CLCoord(from.fCol+dCol, from.fRow+dRow)); }
void TryMoves(int piece, const CLCoord & from, int dCol, int dRow);
void TryMoves(int piece, const CLCoord & from);
void TryMoves(bool omitFrom);
void TryCastle();
CLMoveBuilder * fBuilder;
int fColor;
short * fPieces;
short * fColors;
bool fOmitFrom;
short fTargetUsed[64];
short fTargetAmbiguous[64];
};
void CLMoveGenerator::Generate(int color, short pieces[], short colors[])
{
fBuilder->StartMoveList();
fColor = color;
fPieces = pieces;
fColors = colors;
memset(fTargetUsed, 0, 64*sizeof(short));
memset(fTargetAmbiguous, 0, 64*sizeof(short));
TryMoves(false);
TryMoves(true);
TryCastle();
fBuilder->EndMoveList();
}
void CLMoveGenerator::TryMoves(bool omitFrom)
{
fOmitFrom = omitFrom;
for (int i = 0; i<64; ++i)
if (fColors[i] == fColor)
TryMoves(fPieces[i], CLCoord(i));
}
void CLMoveGenerator::TryMoves(int piece, const CLCoord & from)
{
switch (piece) {
case PAWN: {
int dir = fColor == WHITE ? 1 : -1;
int orig= fColor == WHITE ? 1 : 6;
if (TryMove(piece, from, 0, dir) // Single step always permitted
&& from.fRow == orig // How about a double step?
)
TryMove(piece, from, 0, 2*dir);// Double step
TryMove(piece, from, -1, dir); // Capture left
TryMove(piece, from, 1, dir); // Capture right
break; }
case ROOK:
TryMoves(piece, from, 1, 0);
TryMoves(piece, from, -1, 0);
TryMoves(piece, from, 0, 1);
TryMoves(piece, from, 0, -1);
break;
case KNIGHT:
TryMove(piece, from, 1, 2);
TryMove(piece, from, 2, 1);
TryMove(piece, from, 2, -1);
TryMove(piece, from, 1, -2);
TryMove(piece, from, -1, -2);
TryMove(piece, from, -2, -1);
TryMove(piece, from, -2, 1);
TryMove(piece, from, -1, 2);
break;
case BISHOP:
TryMoves(piece, from, 1, 1);
TryMoves(piece, from, 1, -1);
TryMoves(piece, from, -1, -1);
TryMoves(piece, from, -1, 1);
break;
case QUEEN:
TryMoves(piece, from, 1, 0);
TryMoves(piece, from, -1, 0);
TryMoves(piece, from, 0, 1);
TryMoves(piece, from, 0, -1);
TryMoves(piece, from, 1, 1);
TryMoves(piece, from, 1, -1);
TryMoves(piece, from, -1, -1);
TryMoves(piece, from, -1, 1);
break;
case KING:
TryMove(piece, from, 1, 0);
TryMove(piece, from, -1, 0);
TryMove(piece, from, 0, 1);
TryMove(piece, from, 0, -1);
TryMove(piece, from, 1, 1);
TryMove(piece, from, 1, -1);
TryMove(piece, from, -1, -1);
TryMove(piece, from, -1, 1);
break;
}
}
void CLMoveGenerator::TryMoves(int piece, const CLCoord & from, int dCol, int dRow)
{
CLCoord to(from);
do {
to = CLCoord(to.fCol+dCol, to.fRow+dRow);
} while (TryMove(piece, from, to));
}
bool CLMoveGenerator::TryMove(int piece, const CLCoord & from, const CLCoord & to)
{
int coord = to.fCoord;
if (coord < 0)
return false; // Field does not exist
int color = fColors[coord];
if (color == fColor)
return false; // Field is blocked by own piece
bool take = (color == !fColor); // Field occupied by opponent's piece
if (piece == PAWN) // Pawns move straight, capture diagonally
if (from.fCol != to.fCol) { // Attempted capture
if (!take) { // Field is empty, try en passant
if (from.fRow != (fColor == WHITE ? 4 : 3)) // Must be double step away from opponent's origin
return false;
CLCoord epField(to.fCol, from.fRow);
int epCoord = epField.fCoord;
if (fColors[epCoord] != !fColor || fPieces[epCoord] != PAWN) // Must be opponent's pawn
return false;
take = true; // En passant
}
} else if (take) // Straight move is blocked
return false;
int pieceMask = 1 << piece;
if (fOmitFrom) {
//
// Simplify language model
//
if (fTargetAmbiguous[coord] & pieceMask) // Amiguous move, don't do it
;
else
fBuilder->Move(piece, from, to, take, fOmitFrom);
} else {
fTargetAmbiguous[coord] |= fTargetUsed[coord] & pieceMask;
fTargetUsed[coord] |= pieceMask;
fBuilder->Move(piece, from, to, take, fOmitFrom);
}
return !take; // Don't move further after capture
}
void CLMoveGenerator::TryCastle()
{
int kingCoord;
int kingRookCoord;
int queenRookCoord;
if (fColor == WHITE) {
kingCoord = 4;
kingRookCoord = 7;
queenRookCoord = 0;
} else {
kingCoord = 60;
kingRookCoord = 63;
queenRookCoord = 56;
}
if (fColors[kingCoord] != fColor) // King not in original position
return;
bool kingSide = false;
bool queenSide= false;
if (fColors[kingRookCoord] == fColor && fPieces[kingRookCoord] == ROOK) { // Rook in position
kingSide = true;
for (int i = kingCoord+1; i<kingRookCoord; ++i)
if (fColors[i] != NEUTRAL)
kingSide = false;
}
if (fColors[queenRookCoord] == fColor && fPieces[queenRookCoord] == ROOK) { // Rook in position
queenSide = true;
for (int i = queenRookCoord+1; i<kingCoord; ++i)
if (fColors[i] != NEUTRAL)
queenSide = false;
}
if (kingSide) {
if (queenSide) {
fBuilder->Move(KING, CLCoord(kingCoord), CLCoord(kingCoord+2), false, false);
fBuilder->Move(KING, CLCoord(kingCoord), CLCoord(kingCoord-2), false, false);
} else {
fBuilder->Move(KING, CLCoord(kingCoord), CLCoord(kingCoord+2), false, false);
fBuilder->Move(KING, CLCoord(kingCoord), CLCoord(kingCoord+2), false, true);
}
} else if (queenSide) {
fBuilder->Move(KING, CLCoord(kingCoord), CLCoord(kingCoord-2), false, false);
fBuilder->Move(KING, CLCoord(kingCoord), CLCoord(kingCoord-2), false, true);
}
}
static void ProcessResult (OSErr origStatus, SRRecognitionResult recResult)
{
OSErr status = origStatus;
Size len;
SRLanguageModel resultLM, subLM;
char refCon[5];
if (!status && recResult) {
len = sizeof(resultLM);
status = SRGetProperty (recResult, kSRLanguageModelFormat, &resultLM, &len);
if (!status) {
status = SRGetIndexedItem (resultLM, &subLM, 0);
if (!status) {
len = 4;
status = SRGetProperty (subLM, kSRRefCon, &refCon, &len);
if (!status) {
refCon[4] = 0;
CL_MakeMove(refCon);
}
// release subelement when done with it
SRReleaseObject (subLM);
}
// release resultLM fetched above when done with it
SRReleaseObject (resultLM);
}
}
if (!origStatus) SRReleaseObject (recResult);
}
pascal OSErr HandleSpeechDoneAppleEvent (const AppleEvent *theAEevt, AppleEvent* reply, long refcon)
{
long actualSize;
DescType actualType;
OSErr status = 0;
OSErr recStatus = 0;
SRRecognitionResult recResult = 0;
/* Get status */
status = AEGetParamPtr(theAEevt,keySRSpeechStatus,typeShortInteger,
&actualType, (Ptr)&recStatus, sizeof(status), &actualSize);
/* Get result */
if (!status && !recStatus)
status = AEGetParamPtr(theAEevt,keySRSpeechResult,typeSRSpeechResult,
&actualType, (Ptr)&recResult, sizeof(SRRecognitionResult), &actualSize);
/* Process result */
if (!status)
status = recStatus;
ProcessResult (status, recResult);
return status;
}
static CLSRMoveBuilder * gBuilder;
static CLMoveGenerator * gGenerator;
static void CL_Init()
{
gBuilder = new CLSRMoveBuilder;
#ifdef CHESS_DEBUG
if (getenv("CHESS_DEBUG"))
gGenerator = new CLMoveGenerator(new CLDebugMoveBuilder(gBuilder));
else
#endif
gGenerator = new CLMoveGenerator(gBuilder);
if (gBuilder->OK()) {
AEInstallEventHandler(kAESpeechSuite, kAESpeechDone,
NewAEEventHandlerUPP(HandleSpeechDoneAppleEvent), 0, false);
short myModes = kSRHasFeedbackHasListenModes;
SRSetProperty (gBuilder->Recognizer(), kSRFeedbackAndListeningModes, &myModes, sizeof (myModes));
}
}
void CL_Listen(short color, short pieces[], short colors[])
{
if (!gBuilder)
CL_Init();
gGenerator->Generate(color, pieces, colors);
}
void CL_DontListen()
{
if (gBuilder)
gBuilder->StopListening();
}