#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(); }