/* IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this Apple software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this Apple software. In consideration of your agreement to abide by the following terms, and subject to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in this original Apple software (the "Apple Software"), to use, reproduce, modify and redistribute the Apple Software, with or without modifications, in source and/or binary forms; provided that if you redistribute the Apple Software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the Apple Software. Neither the name, trademarks, service marks or logos of Apple Computer, Inc. may be used to endorse or promote products derived from the Apple Software without specific prior written permission from Apple. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by Apple herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the Apple Software may be incorporated. The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. $RCSfile: ChessListener.cp,v $ Chess Copyright (c) 2000-2001 Apple Computer. All rights reserved. */ #include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> #include <unistd.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); SRReleaseObject(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 bool gIsListening; static bool gIsInited = false; static bool gIsIniting = false; static short gLastColor; static short * gLastPieces; static short * gLastColors; 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_FinishInit() { if (gIsListening) gGenerator->Generate(gLastColor, gLastPieces, gLastColors); gIsInited = true; gIsIniting = false; } void CL_Listen(short color, short pieces[], short colors[]) { gIsListening = true; if (!gBuilder && !gIsIniting) { gIsIniting = true; CL_ScheduleInit(); } if (gIsInited) { gGenerator->Generate(color, pieces, colors); } else { // Store for CL_FinishInit gLastColor = color; gLastPieces = pieces; gLastColors = colors; } } void CL_DontListen() { gIsListening = false; if (gIsInited) gBuilder->StopListening(); } void CL_ShutDown() { if (gIsIniting) /* Shut down later */ return; delete gBuilder; gBuilder = 0; delete gGenerator; gGenerator = 0; gIsListening = gIsInited = false;}