diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | c90c389a8a8d9d8661e9772ec4144c5cf2039f23 (patch) | |
tree | 6d8391395bce9eaea4ad78958617edb20c6a7573 /kreversi | |
download | tdegames-c90c389a8a8d9d8661e9772ec4144c5cf2039f23.tar.gz tdegames-c90c389a8a8d9d8661e9772ec4144c5cf2039f23.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegames@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kreversi')
72 files changed, 7477 insertions, 0 deletions
diff --git a/kreversi/AUTHORS b/kreversi/AUTHORS new file mode 100644 index 00000000..3964c051 --- /dev/null +++ b/kreversi/AUTHORS @@ -0,0 +1,2 @@ +Mario Weilguni <mweilguni@sime.com> Initial coding +Inge Wallin <inge@lysator.liu.se> Cleanups, lots of enhancements diff --git a/kreversi/ChangeLog b/kreversi/ChangeLog new file mode 100644 index 00000000..ad743814 --- /dev/null +++ b/kreversi/ChangeLog @@ -0,0 +1,553 @@ +2006-07-03 Inge Wallin <inge@lysator.liu.se> + + * kreversi.cpp (slotGameOver): Set state back to Ready after the + game is finished, and before showing highscore. + +2006-07-03 Inge Wallin <inge@lysator.liu.se> + + * version.h (KREVERSI_VERSION): Update version to 1.7.1 for KDE + 3.5.4. + +2006-07-03 Inge Wallin <inge@lysator.liu.se> + + * Position.cpp (undoMove): Keep track of score when undoing a + move. + + * qreversigameview.h (removeMove): show game status after removing + a move. + +2006-07-02 Inge Wallin <inge@lysator.liu.se> + + * qreversigameview.cpp (moveMade): Print color Red/Blue in + addition to White/Black into the game view if non-BW color is + chosen in the preferences. + + + ---------------------------------------------------------------- + New start of ChangeLogging + ---------------------------------------------------------------- + + +2005-09-15 Inge Wallin <inge@lysator.liu.se> + + Bump version number for the release of KDE 3.5 + * version.h: Bump version from 1.6 to 1.7 + +2005-04-04 Inge Wallin <inge@lysator.liu.se> + + Fix bug where hint and 'show legal moves' didn't work together. + * board.cpp (showHint): call drawSmallCircle if showLegalMoves is true. + (drawSmallCircle): new private method + New feature: show last move. + * board.cpp (setShowLastMove): new method + + + Refactoring: make showing of legal moves simpler + * board.cpp (showLegalMoves): Take bool for on/off instead of Movelist + (quitShowLegalMoves): removed + (m_legalMovesShowing): new bool member instead of m_legalMoves + * kreversi.cpp (misc): don't call showLegalMoves were not necessary + Fix a bug with 'show legal moves': old ones were never erased. + * board.cpp (showLegalMoves): new method broken out of updateBoard + + Some code cleaning and documentation + * DESIGN: Made documentation up-to-date + * qreversigame.{h,cpp} (updateBoard,turn): removed signals + * qreversigameview.{cpp} (slotNewGame): renamed into newGame + (updateBoard): new method + (updateMovelist): new method (empty yet) + (misc proxy methods): simplified. + + Move over more view stuff to the gameview. + * kreversi.cpp (showMove): Renamed into handleMove, most of it + moved to the view + (slotStateChange): removed slot + (turn(Color), score, stateChange): removed signals + (setState): Do the job of slotStateChange. + * qreversigameview.cpp (moveMade): do the job of showMove + + More control of the view by signals + * kreversi.cpp (showTurn): now catches sig_newGame and sig_update + from the game + (showTurn): new slot + + Let the game view be updated by signals from the game instead of + by explicit calls. + * kreversi.cpp (misc): Don't call updateboard et al. + (showColor): Removed + * qreversigame.cpp (sig_score): Removed. + * qreversigameview.{h,cpp} (slotNewGame, moveMade): new slots + +2005-04-03 Inge Wallin <inge@lysator.liu.se> + + Clean up the signals from the game and change some explicit calls + to update the view into signal/slots instead. + * kreversi.{h, cpp} (showScore): removed + * qreversigame.{h,cpp} (sig_newGame, sig_update): new signals + (gameOver): signal renamed into sig_gameOver + * qreversigameview.{h,cpp} (StatusWidget::setText): new method + (createView): New private method. + (updateView): new slot + (updateStatus): new slot + (setHumanColor): new method. + + + Move the status info from the toolbar to the gameview. + * kreversi.{h,cpp} (StatusWidget): Removed class + (createStatusBar): Removed. + (m_krgame): renamed into m_game + * qreversigameview.{h,cpp} (StatusWidget): Added class + + Move the movelist to the gameview. + + Refactor: Create a new class QReversiGameView that will comprise + the entire view. + * Unfortunately the details of the change got lost in some stupid + mistake of mine. + +2005-04-02 Inge Wallin <inge@lysator.liu.se> + + Fix bug 102890: The result is not put into the higscore if not all + squares are filled at the end of the game + * kreversi.cpp (KReversi): call slotNewGame + +2005-04-01 Inge Wallin <inge@lysator.liu.se> + + Fix bug 102297: I am playing in KReversi as "expert" but it saves + statistics to the "beginner" records + * kreversi.h (m_lowestStrength): Should be uint instead of bool. + +2005-03-31 Inge Wallin <inge@lysator.liu.se> + + Implement wish 102813: Should be able to show last move + * board.{h,cpp} (m_showLastMove, lastMoveShown): new members + (setShowLastMove, showLastMove): new methods + (updateBoard): show last move. + * kreversi.{h,cpp} (showLastMoveAction): new toggleaction + (slotShowLastMove): new slot + * kreversiui.rc (show_last_move): new action + + Some consecutive small, but important changes (latest at the top). + * Position::undoMove(): new method. + * Remove m_lastPosition from class Game. + * Rename makeMove() to doMove() and takeBackMove() to undoMove(). + + Big changes in the lower levels of the program. Mostly + simplifications. + * Move.h, Move.cpp (SimpleMove): renamed from Move + (Move): new class with undo information + * Position.h, Position.cpp: allow Move and SimpleMove in various + places. + * Game.h, Game.cpp: allow Move and SimpleMove in various places + +2005-03-30 Inge Wallin <inge@lysator.liu.se> + + Continue on wish 82900 + * kreversiui.rc (viewToolBar): new toolbar for the views. + * kreversi.{h,cpp} (showLegalMovesAction): new toggleaction + (slotShowLegalMOves): new slot + (misc): check status of toggle action before showing legal moves + NOTE: This change adds a new toolbar. Before testing you must + make install. + + ---------------------------------------------------------------- + + Start on wish 82900: Show possible moves in the current position + * Move.{h,cpp} (Move): new copy constructor + * Move.h (MoveList): new type + * qreversigame.h (position): new method. + * Position.{h,cpp} (generateMoves): new method + * board.{h,cc} (showLegalMoves, quitShowLegalMoves): new methods + (setMarks): Show also legal moves. + * kreversi.cpp: call showLegalMoves() in various places. + + ---------------------------------------------------------------- + + Implement wish 82517: show moves of the game in a view + * Game.h (Game): Make members protected. + (asString): new method + * qreversigame.cpp (makeMove): emit new signal sig_move . + * kreversi.{h,cpp} (m_movesView): new member + (showMove): new slot + +2005-03-29 Inge Wallin <inge@lysator.liu.se> + + Implement wish 82519: Label the board with A-H, 1-8 + * board.cpp (OFFSET): new macro + (m_marksShowing): new member + (setMarks): new method + (mousePressEvent): take into account offset. + (updateBoard): draw markings if m_marksShowing is true + (drawOnePiece): take into account offset. + (adjustSize): take into account markings + + Some cleaning + + ---------------------------------------------------------------- + + Move KReversiGame out to its own file, and remove it. + * qreversigame.{h,cpp}: new files. + (class): Inherit from Game instead of containing it. + (signal score): Rename into sig_score(). + * Makefile.am: include new files. + * board.cpp: Remove class KReversiGame + (all methods): Rename to QReversiGame + + Code cleaning + * Game.{h,cpp} (~Game): new method + +2005-03-28 Inge Wallin <inge@lysator.liu.se> + + * kreversi.cpp (KReversi): Fix faulty connect(). + + +================================================================ + KDE 3.4 released +================================================================ + + +2005-02-18 Inge Wallin <ingwa@dhcp-254-182.lkpg.cendio.se> + + * version.h (KREVERSI_VERSION): Bumped version to 1.6 + +2004-10-31 Inge Wallin <inge@lysator.liu.se> + + Better fix for bug 91055. + * kreversi.cpp (slotNewGame): Reimplement dialog using + KMessageBox::warningYesNo(). This solves the FIXME in the header. + +2004-10-15 Inge Wallin <inge@lysator.liu.se> + + Fix bug 90472: KReversi: When you interrupt the computers move and + then switch sides, the program gets confused + * kreversi.cpp (slotSwitchSides): Don't allow the user to switch + sides if the computers move is interrupted. + +2004-10-11 Inge Wallin <inge@lysator.liu.se> + + Code cleaning + * kreversi.{h,cpp}: Make all members follow the m_ convention. + Also added some comments. + + ----------------- CVS commit on stuff below -------------------- + + Fix bug 91055 - KReversi: If you start a new game when a game is + playing, the user is never asked for confirmation. + * kreversi.cpp (slotNewGame): Show a dialog that asks for + confirmation from the user. + +2004-10-09 Inge Wallin <inge@lysator.liu.se> + + Fix bug 90203: KReversi: It should be visible when the user + interrupts the computers thinking. + * kreversi.cpp (slotInterrupt): call showTurn(). + (showTurn): Show "(interrupted)" if it is. + NOTE: This fix can't be backported easily since there is a string + freeze for BRANCH_3_3. + + ----------------- CVS commit on stuff below -------------------- + + Fix a bug that made the score unset at startup. + * kreversi.cpp (KReversi): show the score at startup. + + ----------------- CVS commit on stuff below -------------------- + + Finally make KReversi a proper Model/View program (step I.4 and + I.5 from the plan in the TODO file). + * board.{h,cpp} (KReversiGame): new class + * board.{h,cpp} (Board): new name KReversiBoardView + * Lots of minor cleanup + * DESIGN: (class diagram): new info + + ----------------- CVS commit on stuff below -------------------- + + Some minor cleanup. + +2004-10-03 Inge Wallin <inge@lysator.liu.se> + + * DESIGN: New document + + ----------------- CVS commit on stuff below -------------------- + + Simplify saving of the game + * Game.{h,cpp} (move(uint)): New method. + * kreversi.cpp (saveGame): Use the new method, and don't call + loadGame to restore the Game object. + +2004-09-29 Inge Wallin <inge@lysator.liu.se> + + Continue to make KReversi a proper model/view program: + Step I.1 of the plan (see TODO): Fix the class Game + * Game.h (Game): Convert to store moves instead of positions. + * Game.cpp (Game): Code cleanup and convert as above. + * Game.{h,cpp}: Follow naming conventions from the rest of the + program. + * Position.{h,cpp}: Follow naming conventions from the rest of the + program. + * Position.{h,cpp} (Position::operator=): new method. + (Position::makeMove): new method. + + Added myself in the credits in the about window. + (Will add myself to the real authors when we have KGame and + network play ready. :-) ) + +2004-09-27 Inge Wallin <inge@lysator.liu.se> + + Continue to make KReversi a proper model/view program: + * Transfer ownership of Game and Engine to kreversi from Board. + board.h, board.cpp, kreversi.h, kreversi.cpp: lots of changes. + + Some other cleanup: + * SuperEngine.h (interrupt): renamed to interrupted() + +2004-09-26 Inge Wallin <inge@lysator.liu.se> + + Fix bug 90195: KReversi: Changing the skill level late in a game + doesn't count as cheating: + * board.h (Board::m_lowestStrength): new member + * board.cpp (Board::newGame): set m_lowestStrength + * board.cpp (Board::setStrength): update m_lowestStrength and + update highscore type. + + Fix Bug 90190: KReversi: Switch sides and then Undo gets the + program out of sync. + * board.cpp (doUndo): If it is the computers turn to move after an + undo, call computerMakeMove(). + (doUndo): Fix repainting so that it looks nice. + +2004-09-25 Inge Wallin <inge@lysator.liu.se> + + Transfer the rest of the slots for KActions to kreversi.cpp + * Board::interrupt() -> KReversi::slotInterrupt() + * Board::doContinue() -> KReversi::slotContinue() + + Rename some slots for clarity + * KReversi::switchSides() -> KReversi::slotSwitchSides() + * KReversi::showSettings() -> KReversi::slotEditSettings() + + Make a trivial function inline: + * Board::interrupt() + +2004-09-24 Inge Wallin <inge@lysator.liu.se> + + Start the work to port KReversi to KGame/Kplayer: + + Transfer the slots for most KActions to kreversi.cpp + * Board::undo() -> KReversi::slotUndo() + (Board::doUndo()): Do the real work of undoing. + * Board->hint() -> KReversi::slotHint() + (Board::showHint): do the actual work of showing the hint. + + Rename some slots for clarity + * KReversi::newGame -> KReversi::slotNewGame + * KReversi::openGame -> KReversi::slotOpenGame + * KReversi::save -> KReversi::slotSave + + Make some trivial functions inline: + * Board::whoseTurn() + * Board::moveNumber() + * Board::score(Color) + * Board::interrupted() + * Board::strength() + +2004-09-23 Anne-Marie Mahfouf <annemarie.mahfouf@free.fr> + + Cleaned some previously left lines of code in board.cpp + Change CustomAdditions=false back in prefs.kcfg to fix compilation + Tested Inges fix and found it works well. + +2004-09-22 Inge Wallin <inge@lysator.liu.se> + + Fix bug 89829: "KReversi: When you save a game, the color for + Human and Computer is not saved" again. See the discussion on the + KDE bugzilla for details + (http://bugs.kde.org/show_bug.cgi?id=89829). + * board.cpp (Board::saveGame): Save m_humanColor as HumanColor. + * Remove saving of the side to move since this is implicit + anyway. + * (Board::loadGame): Fix loading of m_humanColor and + m_competitiveGame + * Fix emit of signal turn, and the condition to call + computerMakeMove(). + * prefs_addons.h: Removed + + +2004-09-18 Anne-Marie Mahfouf (ChangeLog entry by Inge Wallin) + + Fix bug 89829. (See above, though) + * prefs_addons.h: New file + * board.cpp (saveGame): Some changes + +2004-09-18 Inge Wallin <inge@lysator.liu.se> + + Some cleaning: remove unused members, add m_ to members and some + comments. + * Engine.h (Engine::lastYield): removed + * Board.h (Board::nopaint): removed + * kreversi.{h,cpp} (KReversi::board): renamed into m_board. + +2004-09-17 Inge Wallin <inge@lysator.liu.se> + + Some further cleanup: + * board.{h,cpp} (game, engine, human): renamed into m_game, + m_engine and m_humanColor. + +2004-09-16 Inge Wallin <inge@lysator.liu.se> + + Support Casual and Competitive play: + * SuperEngine.cpp (SuperEngine::computeMove()): new parameter + 'competitive' + * Engine.cpp (Engine::computeMove()): new parameter 'competitive' + * board.cpp (m_competitiveGame): new member + (saveGame, loadGame): Save competitive in config file. + * kreversi.cpp (slotGameEnded): Only store result in highscore + file if the game was competitive all the time. + * kreversi.kcfg (CompetitiveGameChoice): new setting + * settings.ui: redesigned + + +2004-08-17 Inge Wallin <inge@lysator.liu.se> + + Finish the big code cleanup: + * board.h, board.cpp: cleaned up + * highscores.h, highscores.cpp, + * kzoommainwindow.h, kzoommainwindow.cpp: + Converted to same coding style as rest of kreversi. + + +2004-08-16 Inge Wallin <inge@lysator.liu.se> + + Continue the big code cleanup: + * Engine.h, Engine.cpp + * kreversi.h kreversi.cpp + + +2004-08-15 Inge Wallin <inge@lysator.liu.se> + + Continue the big code cleanup: + * SuperEngine.h, SuperEngine.cpp + * Game.h Game.cpp + +2004-08-14 Inge Wallin <inge@lysator.liu.se> + + Start of the big code cleanup: + * Move.h, Move.cpp + * Score.h, Score.cpp + * main.cpp + * Position.h, Position.cpp + + Step 2 in the plan to use KGame from libkdegames: + * Code cleaning: Add some comments, reduce complexity, improve + indentation + * Add m_to_move to class Position. + + + Step 1 in the plan to use KGame from libkdegames: + * Code cleaning: Change "enum Player" into "enum Color", since that + is really what it describes. + +2004-06-29 (1.5) Nicolas Hadacek <hadacek@kde.org> + * use KZoomMainWindow + +2004-05-29 (1.4) Nicolas Hadacek <hadacek@kde.org> + * fix statusbar + cleanup code + * use notify framework for sounds + +1999-06-20 Mario Weilguni <mweilguni@kde.org> + * fixed bugs with those damned KStdDirs + * removed the private wallpapers and use the KDE ones instead + * use kimgio + * fixed locating toolbar icons + * compiles now with --enable-final + +1999-06-16 Mario Weilguni <mweilguni@kde.org> + * adapted to the upcoming KDE-2 + +1999-02-01 Mario Weilguni <mweilguni@kde.org> + * fixed a warning (egcs) + +1.0 + * I finally decided that its stable enough. This is the final + version (if no further bugs are detected and I do not have a + good idea what to improve) + + * ChangeLog reversed + +0.6.6 + * [Robert Williams] Changed Reversi.kdelnk to kreversi.kdelnk + * [Robert Williams] Add -caption "%c" to kreversi.kdelnk + * [Robert Williams] Added kapp->getCaption() + * [Robert Williams] getHelpMenu(true, 0) -> Uses own About dialog + +0.6.5 Support for non GNUC++ compilers. + +0.6.4 fixed that get-hit-and-then-doubleclick bug + + fixed a bug that caused the computer to switch sides if no + computer move is possible instead of getting another human move + +0.6.3 sound fix: when the animation is finished, the correct piece gets + redrawn before doing a sound-sync (how could I ever believe 0.6.2 + would be the last change :-) + +0.6.2 animation fixed (hope this will be the last change) + +0.6.1 fixed that newly introduced highscore bug + computer continues now if a game was saved while thinking + +0.6: better integration into new FSSTND + fixes for new kdecore + layout management for all dialogs + mini-icon and icon + locale-strings changed - partial translation required for + other languages than english and german + removed both the kfixedtopwidget and ktablistbox + drawing a border around the reversi board + session-management - what an overkill for kreversi :-)) + you can save (and load) exactly ONE game + + +0.5: added klocale to support translation + added german translation + fixed a few bugs + tried to remove absolute widget placing/sizing + removed the date field from the HOF + grayscale support + fixed those CPU busy bug + removed the -finline-functions optimize flag (not portable) + ported to new KDE file system standards (well, mostly) + fixed segfaulting on exit + +0.4: interims release - no changelog + +0.3: Sound support (using libmediatool) + When switching sides, the Quit -> the computer made on move. FIXED + Fixed a few typos + Cursor changes when thinking + +0.2.1: times(NULL) does not work with FreeBSD (fixed) + fixed a bug (reported by Stephan Kulow) where pixmaps of pieces + are not initialized properly (could't reproduce the bug with + my system) + New "About" dialog showing all (well, most) of the contributors + All xpm's have now 8 bits per color component instead of 16. + 16 bits seems to confuse the XPM loader of Qt. + +0.2: better pieces + animations + fixed a small bug: when someone made it in the hall of fame, + he was'nt notified of this + some improvements in the Hall Of Fame + +0.1.2: background color selectable via dialog + background pixmaps implemented, selectable via menu + background pixmaps are scaled to fit size + pieces are drawn at runtime instead of pixmaps + some accelerators added + toolbar buttons for help and hint added + +0.1.1: now pixmaps are installed + a kdelnk file is installed + version numbering changed + +0.1 : First release diff --git a/kreversi/DESIGN b/kreversi/DESIGN new file mode 100644 index 00000000..d2a13202 --- /dev/null +++ b/kreversi/DESIGN @@ -0,0 +1,121 @@ +This file describes the overall design of KReversi. Some of the +classes have different names at this point, but that is indicated by a +(now: XXX) tag. These names will be changed when the KDE project has +converted from CVS to Subversion since Subversion has so much better +support for renaming files and directories. + +Almost all of the classes hold more members than are mentioned here, +but those are implementation details and would only obscure the +overall picture. This file is only to give a helicopter view of the +program, not to serve as detailed documentation. + + +Classes +======= + +Class Diagram (See details below) +------------- + +KReversi----------------------------------------------+ + | | +--------------------------------+ | + | +-----------------+ | | + | | | | + | v v v + | QReversiGameView Engine StatusBar + | | | | (shows whose turn it is) + | +- - - - - - - + | +----------+ + | | v v + | +- - - -QReversiBoardView Other widgets + | | | (movelist, score) + v v v +QReversiGame + X + v + Game + I +=========+ + I I + v v +Position Move[] + + +Legend: +XXXXXXXX> Inherits +========> Contains +--------> Ownership pointer +- - - - > Reference pointer + + +Details +------- + +ReversiPosition (now: Position) + Holds a Reversi position. This is the object that implements the + Reversi rules. + + +ReversiMove (now: Move) + A move in a Reversi game. + + +ReversiGame (now: Game) + Holds all the moves of the game being played. + Contains: ReversiPosition Holds the current position. + Move[60] + FIXME: Remove the ReversiPosition and let it be implicit? + FIXME: Implement variations (later) + + +Engine + Can generate a move, given a ReversiPosition. + + +QReversiGame + The "document" for KReversi. + Handles a game being played and sends signals to all its views + when something changes. Basically, the only difference between + this class and the more basic ReversiGame is that it sends signals + to the views. + Inherits: ReversiGame The actual game being played + + +QReversiGameView (status: PLANNED) + + Shows a view of a complete game. Currently this comprises a board + view, a listbox with a list of the moves, two status widgets + showing the current score. + + Contains: *QReversiBoardView + *QListBox + *StatusWidget (two of them) + + +QReversiBoardView + + A view for a Reversi board. The rest of the game view is + implemented in the class QReversiGameView. + + Inherits: QWidget. + Contains: *QReversiGame (not owner) + + FIXME: Enhance the view with timing information, clock, etc. + + +KReversi + The main class for the KReversi program + Contains: KActions + *QReversiGame (owner) + *KReversiGameView (owner) + *Engine (owner) + Statusbar + + FIXME: Let the class also own two players. + + +EngineView (status: PLANNED) + View of the internal thought processes of an Engine (trace, etc). + + +Some notes +---------- + +* KReversi follows the Model/View principle strictly. diff --git a/kreversi/Engine.cpp b/kreversi/Engine.cpp new file mode 100644 index 00000000..da7750ce --- /dev/null +++ b/kreversi/Engine.cpp @@ -0,0 +1,787 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni <mweilguni@sime.com>. This file + * is ported from Mats Luthman's <Mats.Luthman@sylog.se> JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Engine produces moves from a Game object through calls to the +// function ComputeMove(). +// +// First of all: this is meant to be a simple example of a game playing +// program. Not everything is done in the most clever way, particularly not +// the way the moves are searched, but it is hopefully made in a way that makes +// it easy to understand. The function ComputeMove2() that does all the work +// is actually not much more than a hundred lines. Much could be done to +// make the search faster though, I'm perfectly aware of that. Feel free +// to experiment. +// +// The method used to generate the moves is called minimax tree search with +// alpha-beta pruning to a fixed depth. In short this means that all possible +// moves a predefined number of moves ahead are either searched or refuted +// with a method called alpha-beta pruning. A more thorough explanation of +// this method could be found at the world wide web at http: +// //yoda.cis.temple.edu:8080/UGAIWWW/lectures96/search/minimax/alpha-beta.html +// at the time this was written. Searching for "minimax" would also point +// you to information on this subject. It is probably possible to understand +// this method by reading the source code though, it is not that complicated. +// +// At every leaf node at the search tree, the resulting position is evaluated. +// Two things are considered when evaluating a position: the number of pieces +// of each color and at which squares the pieces are located. Pieces at the +// corners are valuable and give a high value, and having pieces at squares +// next to a corner is not very good and they give a lower value. In the +// beginning of a game it is more important to have pieces on "good" squares, +// but towards the end the total number of pieces of each color is given a +// higher weight. Other things, like how many legal moves that can be made in a +// position, and the number of pieces that can never be turned would probably +// make the program stronger if they were considered in evaluating a position, +// but that would make things more complicated (this was meant to be very +// simple example) and would also slow down computation (considerably?). +// +// The member m_board[10][10]) holds the current position during the +// computation. It is initiated at the start of ComputeMove() and +// every move that is made during the search is made on this board. It should +// be noted that 1 to 8 is used for the actual board, but 0 and 9 can be +// used too (they are always empty). This is practical when turning pieces +// when moves are made on the board. Every piece that is put on the board +// or turned is saved in the stack m_squarestack (see class SquareStack) so +// every move can easily be reversed after the search in a node is completed. +// +// The member m_bc_board[][] holds board control values for each square +// and is initiated by a call to the function private void SetupBcBoard() +// from Engines constructor. It is used in evaluation of positions except +// when the game tree is searched all the way to the end of the game. +// +// The two members m_coord_bit[9][9] and m_neighbor_bits[9][9] are used to +// speed up the tree search. This goes against the principle of keeping things +// simple, but to understand the program you do not need to understand them +// at all. They are there to make it possible to throw away moves where +// the piece that is played is not adjacent to a piece of opposite color +// at an early stage (because they could never be legal). It should be +// pointed out that not all moves that pass this test are legal, there will +// just be fewer moves that have to be tested in a more time consuming way. +// +// There are also two other members that should be mentioned: Score m_score +// and Score m_bc_score. They hold the number of pieces of each color and +// the sum of the board control values for each color during the search +// (this is faster than counting at every leaf node). +// + +// The classes SquareStackEntry and SquareStack implement a +// stack that is used by Engine to store pieces that are turned during +// searching (see ComputeMove()). +// +// The class MoveAndValue is used by Engine to store all possible moves +// at the first level and the values that were calculated for them. +// This makes it possible to select a random move among those with equal +// or nearly equal value after the search is completed. + + +#include <qapplication.h> + +#include "Engine.h" + + +// ================================================================ +// Class ULONG64 + + +#if !defined(__GNUC__) + + +ULONG64::ULONG64() : QBitArray(64) +{ + fill(0); +} + + +// Initialize an ULONG64 from a 32 bit value. +// + +ULONG64::ULONG64( unsigned int value ) : QBitArray(64) +{ + fill(0); + for(int i = 0; i < 32; i++) { + setBit(i, (bool)(value & 1)); + value >>= 1; + } +} + + +// Shift an ULONG64 left one bit. +// + +void ULONG64::shl() +{ + for(int i = 63; i > 0; i--) + setBit(i, testBit(i - 1)); + setBit(0, 0); +} + +#endif + + +// ================================================================ +// Classes SquareStackEntry and SquareStack + + +// A SquareStack is used to store changes to the squares on the board +// during search. + + +inline void SquareStackEntry::setXY(int x, int y) { + m_x = x; + m_y = y; +} + + +SquareStackEntry::SquareStackEntry() +{ + setXY(0,0); +} + + +// ---------------------------------------------------------------- + + +SquareStack::SquareStack() { + init(0); +} + + +SquareStack::SquareStack(int size) { + init(size); +} + + +void SquareStack::resize(int size) +{ + m_squarestack.resize(size); +} + + +// (Re)initialize the stack so that is empty, and at the same time +// resize it to 'size'. +// + +void SquareStack::init(int size) +{ + resize(size); + + m_top = 0; + for (int i = 0; i < size; i++) + m_squarestack[i].setXY(0,0); +} + + + +inline SquareStackEntry SquareStack::Pop() +{ + return m_squarestack[--m_top]; +} + + +inline void SquareStack::Push(int x, int y) +{ + m_squarestack[m_top].m_x = x; + m_squarestack[m_top++].m_y = y; +} + + +// ================================================================ +// Class MoveAndValue + + +// Class MoveAndValue aggregates a move with its value. +// + + +inline void MoveAndValue::setXYV(int x, int y, int value) +{ + m_x = x; + m_y = y; + m_value = value; +} + + +MoveAndValue::MoveAndValue() +{ + setXYV(0,0,0); +} + + +MoveAndValue::MoveAndValue(int x, int y, int value) +{ + setXYV(x, y, value); +} + + +// ================================================================ +// The Engine itself + + +// Some special values used in the search. +const int Engine::LARGEINT = 99999; +const int Engine::ILLEGAL_VALUE = 8888888; +const int Engine::BC_WEIGHT = 3; + + +Engine::Engine(int st, int sd) : SuperEngine(st, sd) +{ + SetupBcBoard(); + SetupBits(); +} + + +Engine::Engine(int st) : SuperEngine(st) +{ + SetupBcBoard(); + SetupBits(); +} + + +Engine::Engine() : SuperEngine(1) +{ + SetupBcBoard(); + SetupBits(); +} + + +// keep GUI alive +void Engine::yield() +{ + qApp->processEvents(); +} + + +// Calculate the best move from the current position, and return it. + +Move Engine::computeMove(Game *game, bool competitive) +{ + Color color; + + // A competitive game is one where we try our damnedest to make the + // best move. The opposite is a casual game where the engine might + // make "a mistake". The idea behind this is not to scare away + // newbies. The member m_competitive is used during search for this + // very move. + m_competitive = competitive; + + // Suppose that we should give a heuristic evaluation. If we are + // close to the end of the game we can make an exhaustive search, + // but that case is determined further down. + m_exhaustive = false; + + // Get the color to calculate the move for. + color = game->toMove(); + if (color == Nobody) + return Move(Nobody, -1, -1); + + // Figure out the current score + m_score.set(White, game->score(White)); + m_score.set(Black, game->score(Black)); + + // Treat the first move as a special case (we can basically just + // pick a move at random). + if (m_score.score(White) + m_score.score(Black) == 4) + return ComputeFirstMove(game); + + // Let there be room for 3000 changes during the recursive search. + // This is more than will ever be needed. + m_squarestack.init(3000); + + // Get the search depth. If we are close to the end of the game, + // the number of possible moves goes down, so we can search deeper + // without using more time. + m_depth = m_strength; + if (m_score.score(White) + m_score.score(Black) + m_depth + 3 >= 64) + m_depth = 64 - m_score.score(White) - m_score.score(Black); + else if (m_score.score(White) + m_score.score(Black) + m_depth + 4 >= 64) + m_depth += 2; + else if (m_score.score(White) + m_score.score(Black) + m_depth + 5 >= 64) + m_depth++; + + // If we are very close to the end, we can even make the search + // exhaustive. + if (m_score.score(White) + m_score.score(Black) + m_depth >= 64) + m_exhaustive = true; + + // The evaluation is a linear combination of the score (number of + // pieces) and the sum of the scores for the squares (given by + // m_bc_score). The earlier in the game, the more we use the square + // values and the later in the game the more we use the number of + // pieces. + m_coeff = 100 - (100* + (m_score.score(White) + m_score.score(Black) + + m_depth - 4)) / 60; + + // Initialize the board that we use for the search. + for (uint x = 0; x < 10; x++) + for (uint y = 0; y < 10; y++) { + if (1 <= x && x <= 8 + && 1 <= y && y <= 8) + m_board[x][y] = game->color(x, y); + else + m_board[x][y] = Nobody; + } + + // Initialize a lot of stuff that we will use in the search. + + // Initialize m_bc_score to the current bc score. This is kept + // up-to-date incrementally so that way we won't have to calculate + // it from scratch for each evaluation. + m_bc_score.set(White, CalcBcScore(White)); + m_bc_score.set(Black, CalcBcScore(Black)); + + ULONG64 colorbits = ComputeOccupiedBits(color); + ULONG64 opponentbits = ComputeOccupiedBits(opponent(color)); + + int maxval = -LARGEINT; + int max_x = 0; + int max_y = 0; + + MoveAndValue moves[60]; + int number_of_moves = 0; + int number_of_maxval = 0; + + setInterrupt(false); + + ULONG64 null_bits; + null_bits = 0; + + // The main search loop. Step through all possible moves and keep + // track of the most valuable one. This move is stored in + // (max_x, max_y) and the value is stored in maxval. + m_nodes_searched = 0; + for (int x = 1; x < 9; x++) { + for (int y = 1; y < 9; y++) { + // Don't bother with non-empty squares and squares that aren't + // neighbors to opponent pieces. + if (m_board[x][y] != Nobody + || (m_neighbor_bits[x][y] & opponentbits) == null_bits) + continue; + + + int val = ComputeMove2(x, y, color, 1, maxval, + colorbits, opponentbits); + + if (val != ILLEGAL_VALUE) { + moves[number_of_moves++].setXYV(x, y, val); + + // If the move is better than all previous moves, then record + // this fact... + if (val > maxval) { + + // ...except that we want to make the computer miss some + // good moves so that beginners can play against the program + // and not always lose. However, we only do this if the + // user wants a casual game, which is set in the settings + // dialog. + int randi = m_random.getLong(7); + if (maxval == -LARGEINT + || m_competitive + || randi < (int) m_strength) { + maxval = val; + max_x = x; + max_y = y; + + number_of_maxval = 1; + } + } + else if (val == maxval) + number_of_maxval++; + } + + // Jump out prematurely if interrupt is set. + if (interrupted()) + break; + } + } + + // long endtime = times(&tmsdummy); + + // If there are more than one best move, the pick one randomly. + if (number_of_maxval > 1) { + int r = m_random.getLong(number_of_maxval) + 1; + int i; + + for (i = 0; i < number_of_moves; i++) { + if (moves[i].m_value == maxval && --r <= 0) + break; + } + + max_x = moves[i].m_x; + max_y = moves[i].m_y; + } + + // Return a suitable move. + if (interrupted()) + return Move(Nobody, -1, -1); + else if (maxval != -LARGEINT) + return Move(color, max_x, max_y); + else + return Move(Nobody, -1, -1); +} + + +// Get the first move. We can pick any move at random. +// + +Move Engine::ComputeFirstMove(Game *game) +{ + int r; + Color color = game->toMove(); + + r = m_random.getLong(4) + 1; + + if (color == White) { + if (r == 1) return Move(color, 3, 5); + else if (r == 2) return Move(color, 4, 6); + else if (r == 3) return Move(color, 5, 3); + else return Move(color, 6, 4); + } + else { + if (r == 1) return Move(color, 3, 4); + else if (r == 2) return Move(color, 5, 6); + else if (r == 3) return Move(color, 4, 3); + else return Move(color, 6, 5); + } +} + + +// Play a move at (xplay, yplay) and generate a value for it. If we +// are at the maximum search depth, we get the value by calling +// EvaluatePosition(), otherwise we get it by performing an alphabeta +// search. +// + +int Engine::ComputeMove2(int xplay, int yplay, Color color, int level, + int cutoffval, ULONG64 colorbits, + ULONG64 opponentbits) +{ + int number_of_turned = 0; + SquareStackEntry mse; + Color opponent = ::opponent(color); + + m_nodes_searched++; + + // Put the piece on the board and incrementally update scores and bitmaps. + m_board[xplay][yplay] = color; + colorbits |= m_coord_bit[xplay][yplay]; + m_score.inc(color); + m_bc_score.add(color, m_bc_board[xplay][yplay]); + + // Loop through all 8 directions and turn the pieces that can be turned. + for (int xinc = -1; xinc <= 1; xinc++) + for (int yinc = -1; yinc <= 1; yinc++) { + if (xinc == 0 && yinc == 0) + continue; + + int x, y; + + for (x = xplay + xinc, y = yplay + yinc; m_board[x][y] == opponent; + x += xinc, y += yinc) + ; + + // If we found the end of a turnable row, then go back and turn + // all pieces on the way back. Also push the squares with + // turned pieces on the squarestack so that we can undo the move + // later. + if (m_board[x][y] == color) + for (x -= xinc, y -= yinc; x != xplay || y != yplay; + x -= xinc, y -= yinc) { + m_board[x][y] = color; + colorbits |= m_coord_bit[x][y]; + opponentbits &= ~m_coord_bit[x][y]; + + m_squarestack.Push(x, y); + + m_bc_score.add(color, m_bc_board[x][y]); + m_bc_score.sub(opponent, m_bc_board[x][y]); + number_of_turned++; + } + } + + int retval = -LARGEINT; + + // If we managed to turn at least one piece, then (xplay, yplay) was + // a legal move. Now find out the value of the move. + if (number_of_turned > 0) { + + // First adjust the number of pieces for each side. + m_score.add(color, number_of_turned); + m_score.sub(opponent, number_of_turned); + + // If we are at the bottom of the search, get the evaluation. + if (level >= m_depth) + retval = EvaluatePosition(color); // Terminal node + else { + int maxval = TryAllMoves(opponent, level, cutoffval, opponentbits, + colorbits); + + if (maxval != -LARGEINT) + retval = -maxval; + else { + + // No possible move for the opponent, it is colors turn again: + retval = TryAllMoves(color, level, -LARGEINT, colorbits, opponentbits); + + if (retval == -LARGEINT) { + + // No possible move for anybody => end of game: + int finalscore = m_score.score(color) - m_score.score(opponent); + + if (m_exhaustive) + retval = finalscore; + else { + // Take a sure win and avoid a sure loss (may not be optimal): + + if (finalscore > 0) + retval = LARGEINT - 65 + finalscore; + else if (finalscore < 0) + retval = -(LARGEINT - 65 + finalscore); + else + retval = 0; + } + } + } + } + + m_score.add(opponent, number_of_turned); + m_score.sub(color, number_of_turned); + } + + // Undo the move. Start by unturning the turned pieces. + for (int i = number_of_turned; i > 0; i--) { + mse = m_squarestack.Pop(); + m_bc_score.add(opponent, m_bc_board[mse.m_x][mse.m_y]); + m_bc_score.sub(color, m_bc_board[mse.m_x][mse.m_y]); + m_board[mse.m_x][mse.m_y] = opponent; + } + + // Now remove the new piece that we put down. + m_board[xplay][yplay] = Nobody; + m_score.sub(color, 1); + m_bc_score.sub(color, m_bc_board[xplay][yplay]); + + // Return a suitable value. + if (number_of_turned < 1 || interrupted()) + return ILLEGAL_VALUE; + else + return retval; +} + + +// Generate all legal moves from the current position, and do a search +// to see the value of them. This function returns the value of the +// most valuable move, but not the move itself. +// + +int Engine::TryAllMoves(Color opponent, int level, int cutoffval, + ULONG64 opponentbits, ULONG64 colorbits) +{ + int maxval = -LARGEINT; + + // Keep GUI alive by calling the event loop. + yield(); + + ULONG64 null_bits; + null_bits = 0; + + for (int x = 1; x < 9; x++) { + for (int y = 1; y < 9; y++) { + if (m_board[x][y] == Nobody + && (m_neighbor_bits[x][y] & colorbits) != null_bits) { + int val = ComputeMove2(x, y, opponent, level+1, maxval, opponentbits, + colorbits); + + if (val != ILLEGAL_VALUE && val > maxval) { + maxval = val; + if (maxval > -cutoffval || interrupted()) + break; + } + } + } + + if (maxval > -cutoffval || interrupted()) + break; + } + + if (interrupted()) + return -LARGEINT; + + return maxval; +} + + +// Calculate a heuristic value for the current position. If we are at +// the end of the game, do this by counting the pieces. Otherwise do +// it by combining the score using the number of pieces, and the score +// using the board control values. +// + +int Engine::EvaluatePosition(Color color) +{ + int retval; + + Color opponent = ::opponent(color); + + int score_color = m_score.score(color); + int score_opponent = m_score.score(opponent); + + if (m_exhaustive) + retval = score_color - score_opponent; + else { + retval = (100-m_coeff) * + (m_score.score(color) - m_score.score(opponent)) + + m_coeff * BC_WEIGHT * (m_bc_score.score(color) + - m_bc_score.score(opponent)); + } + + return retval; +} + + +// Calculate bitmaps for each square, and also for neighbors of each +// square. +// + +void Engine::SetupBits() +{ + //m_coord_bit = new long[9][9]; + //m_neighbor_bits = new long[9][9]; + + ULONG64 bits = 1; + + // Store a 64 bit unsigned it with the corresponding bit set for + // each square. + for (int i=1; i < 9; i++) + for (int j=1; j < 9; j++) { + m_coord_bit[i][j] = bits; +#if !defined(__GNUC__) + bits.shl(); +#else + bits *= 2; +#endif + } + + // Store a bitmap consisting of all neighbors for each square. + for (int i=1; i < 9; i++) + for (int j=1; j < 9; j++) { + m_neighbor_bits[i][j] = 0; + + for (int xinc=-1; xinc<=1; xinc++) + for (int yinc=-1; yinc<=1; yinc++) { + if (xinc != 0 || yinc != 0) + if (i + xinc > 0 && i + xinc < 9 && j + yinc > 0 && j + yinc < 9) + m_neighbor_bits[i][j] |= m_coord_bit[i + xinc][j + yinc]; + } + } +} + + +// Set up the board control values that will be used in evaluation of +// the position. +// + +void Engine::SetupBcBoard() +{ + // JAVA m_bc_board = new int[9][9]; + + for (int i=1; i < 9; i++) + for (int j=1; j < 9; j++) { + if (i == 2 || i == 7) + m_bc_board[i][j] = -1; + else + m_bc_board[i][j] = 0; + + if (j == 2 || j == 7) + m_bc_board[i][j] -= 1; + } + + m_bc_board[1][1] = 2; + m_bc_board[8][1] = 2; + m_bc_board[1][8] = 2; + m_bc_board[8][8] = 2; + + m_bc_board[1][2] = -1; + m_bc_board[2][1] = -1; + m_bc_board[1][7] = -1; + m_bc_board[7][1] = -1; + m_bc_board[8][2] = -1; + m_bc_board[2][8] = -1; + m_bc_board[8][7] = -1; + m_bc_board[7][8] = -1; +} + + +// Calculate the board control score. +// + +int Engine::CalcBcScore(Color color) +{ + int sum = 0; + + for (int i=1; i < 9; i++) + for (int j=1; j < 9; j++) + if (m_board[i][j] == color) + sum += m_bc_board[i][j]; + + return sum; +} + + +// Calculate a bitmap of the occupied squares for a certain color. +// + +ULONG64 Engine::ComputeOccupiedBits(Color color) +{ + ULONG64 retval = 0; + + for (int i=1; i < 9; i++) + for (int j=1; j < 9; j++) + if (m_board[i][j] == color) retval |= m_coord_bit[i][j]; + + return retval; +} + diff --git a/kreversi/Engine.h b/kreversi/Engine.h new file mode 100644 index 00000000..a84be895 --- /dev/null +++ b/kreversi/Engine.h @@ -0,0 +1,245 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni <mweilguni@sime.com>. This file + * is ported from Mats Luthman's <Mats.Luthman@sylog.se> JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Engine produces moves from a Game object through calls to the +// function ComputeMove(). +// +// First of all: this is meant to be a simple example of a game playing +// program. Not everything is done in the most clever way, particularly not +// the way the moves are searched, but it is hopefully made in a way that makes +// it easy to understand. The function ComputeMove2() that does all the work +// is actually not much more than a hundred lines. Much could be done to +// make the search faster though, I'm perfectly aware of that. Feel free +// to experiment. +// +// The method used to generate the moves is called minimax tree search with +// alpha-beta pruning to a fixed depth. In short this means that all possible +// moves a predefined number of moves ahead are either searched or refuted +// with a method called alpha-beta pruning. A more thorough explanation of +// this method could be found at the world wide web at http: +// //yoda.cis.temple.edu:8080/UGAIWWW/lectures96/search/minimax/alpha-beta.html +// at the time this was written. Searching for "minimax" would also point +// you to information on this subject. It is probably possible to understand +// this method by reading the source code though, it is not that complicated. +// +// At every leaf node at the search tree, the resulting position is evaluated. +// Two things are considered when evaluating a position: the number of pieces +// of each color and at which squares the pieces are located. Pieces at the +// corners are valuable and give a high value, and having pieces at squares +// next to a corner is not very good and they give a lower value. In the +// beginning of a game it is more important to have pieces on "good" squares, +// but towards the end the total number of pieces of each color is given a +// higher weight. Other things, like how many legal moves that can be made in a +// position, and the number of pieces that can never be turned would probably +// make the program stronger if they were considered in evaluating a position, +// but that would make things more complicated (this was meant to be very +// simple example) and would also slow down computation (considerably?). +// +// The member m_board[10][10]) holds the current position during the +// computation. It is initiated at the start of ComputeMove() and +// every move that is made during the search is made on this board. It should +// be noted that 1 to 8 is used for the actual board, but 0 and 9 can be +// used too (they are always empty). This is practical when turning pieces +// when moves are made on the board. Every piece that is put on the board +// or turned is saved in the stack m_squarestack (see class SquareStack) so +// every move can easily be reversed after the search in a node is completed. +// +// The member m_bc_board[][] holds board control values for each square +// and is initiated by a call to the function private void SetupBcBoard() +// from Engines constructor. It is used in evaluation of positions except +// when the game tree is searched all the way to the end of the game. +// +// The two members m_coord_bit[9][9] and m_neighbor_bits[9][9] are used to +// speed up the tree search. This goes against the principle of keeping things +// simple, but to understand the program you do not need to understand them +// at all. They are there to make it possible to throw away moves where +// the piece that is played is not adjacent to a piece of opposite color +// at an early stage (because they could never be legal). It should be +// pointed out that not all moves that pass this test are legal, there will +// just be fewer moves that have to be tested in a more time consuming way. +// +// There are also two other members that should be mentioned: Score m_score +// and Score m_bc_score. They hold the number of pieces of each color and +// the sum of the board control values for each color during the search +// (this is faster than counting at every leaf node). +// + +// The classes SquareStackEntry and SquareStack implement a +// stack that is used by Engine to store pieces that are turned during +// searching (see ComputeMove()). +// +// The class MoveAndValue is used by Engine to store all possible moves +// at the first level and the values that were calculated for them. +// This makes it possible to select a random move among those with equal +// or nearly equal value after the search is completed. + +#ifndef __ENGINE__H__ +#define __ENGINE__H__ + +#include "SuperEngine.h" +#include "Position.h" +#include "Game.h" +#include "Move.h" +#include "Score.h" +#include <qmemarray.h> +#include <sys/times.h> +#include <qbitarray.h> + + +// Class ULONG64 is used as a bitmap for the squares. + +#if defined(__GNUC__) +#define ULONG64 unsigned long long int +#else +class ULONG64 : public QBitArray { +public: + ULONG64(); + ULONG64( unsigned int ); + void shl(); +}; +#endif + + +// SquareStackEntry and SquareStack are used during search to keep +// track of turned pieces. + +class SquareStackEntry +{ +public: + SquareStackEntry(); + + void setXY(int x, int y); + +public: + int m_x; + int m_y; +}; + + +class SquareStack +{ +public: + SquareStack(); + SquareStack(int size); + + void resize(int size); + void init(int size); + SquareStackEntry Pop(); + void Push(int x, int y); + +private: + QMemArray<SquareStackEntry> m_squarestack; + int m_top; +}; + + +// Connect a move with its value. + +class MoveAndValue +{ +public: + MoveAndValue(); + MoveAndValue(int x, int y, int value); + + void setXYV(int x, int y, int value); + +public: + int m_x; + int m_y; + int m_value; +}; + + +// The real beef of this program: the engine that finds good moves for +// the computer player. +// +class Engine : public SuperEngine { +public: + Engine(int st, int sd); + Engine(int st); + Engine(); + + Move computeMove(Game *game, bool competitive); + +private: + Move ComputeFirstMove(Game *game); + int ComputeMove2(int xplay, int yplay, Color color, int level, + int cutoffval, + ULONG64 colorbits, ULONG64 opponentbits); + + int TryAllMoves(Color opponent, int level, int cutoffval, + ULONG64 opponentbits, ULONG64 colorbits); + + int EvaluatePosition(Color color); + void SetupBcBoard(); + void SetupBits(); + int CalcBcScore(Color color); + ULONG64 ComputeOccupiedBits(Color color); + + void yield(); + +private: + static const int LARGEINT; + static const int ILLEGAL_VALUE; + static const int BC_WEIGHT; + + Color m_board[10][10]; + int m_bc_board[9][9]; + Score m_score; + Score m_bc_score; + SquareStack m_squarestack; + + int m_depth; + int m_coeff; + int m_nodes_searched; + bool m_exhaustive; + bool m_competitive; + + ULONG64 m_coord_bit[9][9]; + ULONG64 m_neighbor_bits[9][9]; +}; + +#endif diff --git a/kreversi/Game.cpp b/kreversi/Game.cpp new file mode 100644 index 00000000..e389fdd3 --- /dev/null +++ b/kreversi/Game.cpp @@ -0,0 +1,265 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni <mweilguni@sime.com>. This file + * is ported from Mats Luthman's <Mats.Luthman@sylog.se> JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Game represents a complete or incomplete Othello game. It uses +// the classes Score and Move (and internally Position). +// You can make moves, take back one move at a time, reset to initial position +// and get certain data on the current position. + +// Public functions: + +// public Game() +// Creates a game with the initial position. + +// public void Reset() +// Resets to the initial position. + +// public boolean makeMove(Move &move) +// Makes the move m. Returns false if the move is not legal or when called +// with a move where the player is Score.NOBODY. + +// public boolean TakeBackMove() +// Takes back a move. Returns true if not at the initial position. + +// public int GetSquare(int x, int y) +// Returns the piece at (x, y). Returns Score.NOBODY if the square is not +// occupied. + +// public int GetScore(int player) +// Returns the score for player. + +// public Move GetLastMove() +// Returns the last move. Returns null if at the initial position. + +// public boolean MoveIsLegal(Move m) +// Checks if move m is legal. + +// public boolean MoveIsPossible(int player) +// Checks if there is a legal move for player. + +// public boolean MoveIsAtAllPossible() +// Checks if there are any legal moves at all. + +// public int GetMoveNumber() +// Returns move number. + +// public int GetWhoseTurn() +// Returns the player in turn to play (if there are no legal moves +// Score.NOBODY is returned). + +// public Move[] TurnedByLastMove() +// Returns a vector of the squares that were changed by the last move. +// The move that was actually played is at index 0. At the initial +// position the length of the vector returned is zero. (Could be used +// for faster updates of a graphical board). + + +#include <assert.h> + +#include "Game.h" + + + +Game::Game() +{ + newGame(); +} + +Game::~Game() +{ +} + + +// Start a new game and reset the position to before the first move. +// + +void Game::newGame() +{ + m_position.setupStartPosition(); + m_moveNumber = 0; +} + + +// Return the last move made in the game. +// + +Move Game::lastMove() const +{ + // If no moves where made, return a NULL move. + if (m_moveNumber == 0) + return Move(); + + return m_moves[m_moveNumber - 1]; +} + + +Move +Game::move(uint moveNo) const +{ + assert(moveNo < m_moveNumber); + + return m_moves[moveNo]; +} + + + +// Return true if the move is legal in the current position. +// + +bool Game::moveIsLegal(SimpleMove &move) const +{ + return m_position.moveIsLegal(move); +} + + +// Return true if the color can make a move in the current position. + +bool Game::moveIsPossible(Color color) const +{ + return m_position.moveIsPossible(color); +} + + +// Return true if any side can make a move in the current position. +// + +bool Game::moveIsAtAllPossible() const +{ + return m_position.moveIsAtAllPossible(); +} + + +// Make a move in the game, resulting in a new position. +// +// If everything went well, return true. Otherwise return false and +// do nothing. + +bool Game::doMove(Move &move) +{ + Position lastPos = m_position; + + // Some sanity checks. + if (move.color() == Nobody) + return false; + + if (toMove() != move.color()) + return false; + + // Make the move in the position and store it. Don't allow illegal moves. + if (!m_position.doMove(move)) + return false; + + m_moves[m_moveNumber++] = move; + + return true; +} + + +bool Game::doMove(SimpleMove &smove) +{ + Move move(smove); + + return doMove(move); +} + + +// Take back the last move. +// +// Note: The removed move is not remembered, so a redo is not possible. +// + +bool Game::undoMove() +{ + if (m_moveNumber == 0) + return false; + +#if 0 + m_position.setupStartPosition(); + m_moveNumber--; + for (uint i = 0; i < m_moveNumber; i++) + m_position.doMove(m_moves[i]); +#else + m_position.undoMove(m_moves[--m_moveNumber]); +#endif + + return true; +} + + +// ---------------------------------------------------------------- +// Reversi specific methods + + +// Return true if the square at (x, y) was changed during the last move. +// + +bool Game::squareModified(uint x, uint y) const +{ + // If the move number is zero, we want to redraw all squares. + // That's why we return true here. + if (m_moveNumber == 0) + return true; + + return m_moves[m_moveNumber - 1].squareModified(x, y); +} + + +// Return true if the piece at square (x, y) was turned during the last move. +// + +bool Game::wasTurned(uint x, uint y) const +{ + // Nothing turned before the first move. + if (m_moveNumber == 0) + return false; + + Color color = m_position.color(x, y); + + if (color == Nobody) + return false; + + return m_moves[m_moveNumber - 1].wasTurned(x, y); +} diff --git a/kreversi/Game.h b/kreversi/Game.h new file mode 100644 index 00000000..c33a2096 --- /dev/null +++ b/kreversi/Game.h @@ -0,0 +1,143 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni <mweilguni@sime.com>. This file + * is ported from Mats Luthman's <Mats.Luthman@sylog.se> JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Game represents a complete or incomplete Othello game. It uses +// the classes Score and Move (and internally Position). +// You can make moves, take back one move at a time, reset to initial position +// and get certain data on the current position. + +// Public functions: + +// public Game() +// Creates a game with the initial position. + +// public void Reset() +// Resets to the initial position. + +// public boolean MakeMove(Move m) +// Makes the move m. Returns false if the move is not legal or when called +// with a move where the player is Score.NOBODY. + +// public boolean TakeBackMove() +// Takes back a move. Returns true if not at the initial position. + +// public int GetSquare(int x, int y) +// Returns the piece at (x, y). Returns Score.NOBODY if the square is not +// occupied. + +// public int GetScore(int player) +// Returns the score for player. + +// public Move GetLastMove() +// Returns the last move. Returns null if at the initial position. + +// public boolean MoveIsLegal(Move m) +// Checks if move m is legal. + +// public boolean MoveIsPossible(int player) +// Checks if there is a legal move for player. + +// public boolean MoveIsAtAllPossible() +// Checks if there are any legal moves at all. + +// public int GetMoveNumber() +// Returns move number. + +// public int GetWhoseTurn() +// Returns the player in turn to play (if there are no legal moves +// Score.NOBODY is returned). + +// public Move[] TurnedByLastMove() +// Returns a vector of the squares that were changed by the last move. +// The move that was actually played is at index 0. At the initial +// position the length of the vector returned is zero. (Could be used +// for faster updates of a graphical board). + + +#ifndef __GAME__H__ +#define __GAME__H__ + + +#include "Score.h" +#include "Move.h" +#include "Position.h" + + +class Game { +public: + Game(); + ~Game(); + + void newGame(); + + Color color(uint x, uint y) const { return m_position.color(x, y); } + uint score(Color color) const { return m_position.score(color); } + Move lastMove() const; + Move move(uint moveNo) const; + + bool moveIsLegal(SimpleMove &move) const; + bool moveIsPossible(Color color) const; + bool moveIsAtAllPossible() const; + bool doMove(Move &move); + bool doMove(SimpleMove &move); + bool undoMove(); + + const Position &position() const { return m_position; } + uint moveNumber() const { return m_moveNumber; } + Color toMove() const { return m_position.toMove(); } + + // Reversi specific methods + bool squareModified(uint x, uint y) const; + bool wasTurned(uint x, uint y) const; + +protected: + Move m_moves[60]; + Position m_position; // The current position in the game + uint m_moveNumber; +}; + + +#endif diff --git a/kreversi/Makefile.am b/kreversi/Makefile.am new file mode 100644 index 00000000..ca13e36f --- /dev/null +++ b/kreversi/Makefile.am @@ -0,0 +1,101 @@ +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +bin_PROGRAMS = kreversi + +kreversi_SOURCES = \ + kzoommainwindow.cpp \ + Score.cpp \ + Move.cpp \ + Position.cpp \ + Game.cpp \ + qreversigame.cpp \ + qreversigameview.cpp \ + SuperEngine.cpp \ + Engine.cpp \ + board.cpp \ + settings.ui \ + highscores.cpp \ + kreversi.cpp \ + main.cpp \ + prefs.kcfgc +kreversi_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kreversi_LDADD = $(LIB_KDEGAMES) $(LIB_KIO) +kreversi_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +METASOURCES = AUTO + +rcdir = $(kde_datadir)/kreversi +rc_DATA = kreversiui.rc + +noinst_HEADERS = \ + kzoommainwindow.h \ + Engine.h \ + Game.h \ + qreversigame.h \ + qreversigameview.h \ + kreversi.h \ + Move.h \ + board.h \ + Position.h \ + Score.h \ + version.h \ + SuperEngine.h \ + highscores.h + +SUBDIRS = . pics sounds icons + +xdg_apps_DATA = kreversi.desktop +kde_kcfg_DATA = kreversi.kcfg + + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kreversi.pot + +# for system-wide highscore file +DESTBIN = $(DESTDIR)$(bindir)/$(bin_PROGRAMS) +DESTHIGHSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY) +DESTSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY)/$(bin_PROGRAMS).scores + +install-data-local: + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + echo "********************************************************" ;\ + echo "" ;\ + echo "This game is installed sgid \"games\" to use the" ;\ + echo "system-wide highscore file (in "$(HIGHSCORE_DIRECTORY)")." ;\ + echo "" ;\ + echo "If the system-wide highscore file does not exist, it is" ;\ + echo "created with the correct ownership and permissions. See the" ;\ + echo "INSTALL file in \"kdegames/libkdegames/highscore\" for details." ;\ + echo "" ;\ + echo "********************************************************" ;\ + fi + +install-exec-hook: + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + chown $(highscore_user):$(highscore_group) $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + mkdir -p $(DESTHIGHSCORES) && \ + chown $(highscore_user):$(highscore_group) $(DESTHIGHSCORES) \ + && chmod 750 $(DESTHIGHSCORES) \ + || echo "Error: Could not create the highscore directory with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + chown $(highscore_user):$(highscore_group) $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test ${setgid} = true; then \ + chmod 2755 $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + touch $(DESTSCORES) && chown $(highscore_user):$(highscore_group) $(DESTSCORES) \ + && chmod 0660 $(DESTSCORES) \ + || echo "Error: Could not create system-wide highscore file with correct permissions !!" ;\ + fi + diff --git a/kreversi/Move.cpp b/kreversi/Move.cpp new file mode 100644 index 00000000..3a616647 --- /dev/null +++ b/kreversi/Move.cpp @@ -0,0 +1,118 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni <mweilguni@sime.com>. This file + * is ported from Mats Luthman's <Mats.Luthman@sylog.se> JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#include "Move.h" + + +SimpleMove::SimpleMove(Color color, int x, int y) +{ + m_color = color; + m_x = x; + m_y = y; +} + + +SimpleMove::SimpleMove(const SimpleMove &move) +{ + *this = move; +} + + +QString SimpleMove::asString() const +{ + if (m_x == -1) + return QString("pass"); + else + return QString("%1%2").arg(" ABCDEFGH"[m_x]).arg(" 12345678"[m_y]); +} + + +// ================================================================ + + +Move::Move() + : SimpleMove() +{ + m_turnedPieces.clear(); +} + + +Move::Move(Color color, int x, int y) + : SimpleMove(color, x, y) +{ + m_turnedPieces.clear(); +} + + +Move::Move(const Move &move) + : SimpleMove((SimpleMove&) move) +{ + m_turnedPieces.clear(); +} + + +Move::Move(const SimpleMove &move) + : SimpleMove(move) +{ + m_turnedPieces.clear(); +} + + +// ---------------------------------------------------------------- + + +bool Move::squareModified(uint x, uint y) const +{ + return (m_x == (int) x && m_y == (int) y) || wasTurned(x, y); +} + + +bool Move::wasTurned(uint x, uint y) const +{ + // findIndex returns the first index where the item is found, or -1 + // if not found. + return (m_turnedPieces.findIndex(10 * x + y) != -1); +} diff --git a/kreversi/Move.h b/kreversi/Move.h new file mode 100644 index 00000000..e205f279 --- /dev/null +++ b/kreversi/Move.h @@ -0,0 +1,124 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni <mweilguni@sime.com>. This file + * is ported from Mats Luthman's <Mats.Luthman@sylog.se> JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// This file defines the two classes SimpleMove and Move. +// +// The class Move is used to represent an Othello move with a player value +// (see class Score) and a pair of coordinates on an 8x8 Othello board. +// Each coordinate can have values between 1 and 8, inclusive. +// +// The difference between a Move and a SimpleMove is that a SimpleMove +// can be done (performed) in a Position, but a Move can be both done +// and undone. In addition to the info in SimpleMove, the class Move +// stores information that is used in undoing the move and visualizing +// it. +// +// The reason for the class SimpleMove is that it saves memory. The +// class Game stores an array of Moves, since the BoardView needs +// information about which pieces were turned by the move. +// + + +#ifndef __MOVE__H__ +#define __MOVE__H__ + + +#include "qvaluelist.h" +#include "qstring.h" + +#include "Score.h" + + +class Position; + + +class SimpleMove +{ +public: + SimpleMove() { m_color = Nobody; m_x = -1; m_y = -1; } + SimpleMove(Color color, int x, int y); + SimpleMove(const SimpleMove &move); + + //Move &operator=(Move &move); + + Color color() const { return m_color; } + int x() const { return m_x; } + int y() const { return m_y; } + + QString asString() const; + +protected: + Color m_color; + int m_x; + int m_y; +}; + + +// Note: This class is not memory optimized. The list of turned +// pieces can surely be made much smaller. + +class Move : public SimpleMove +{ + friend class Position; + +public: + Move(); + Move(Color color, int x, int y); + Move(const Move &move); + Move(const SimpleMove &move); + + bool squareModified(uint x, uint y) const; + bool wasTurned(uint x, uint y) const; + +private: + QValueList<char> m_turnedPieces; +}; + + +typedef QValueList<Move> MoveList; + + +#endif diff --git a/kreversi/NEWS b/kreversi/NEWS new file mode 100644 index 00000000..8e815ffa --- /dev/null +++ b/kreversi/NEWS @@ -0,0 +1,20 @@ +v0.1pl168: Initial release + +06/14/97 released version 0.1.2 + +06/16/97 released version 0.2 + +06/25/97 released version 0.2.1 + +07/30/97 released version 0.3 + +???????? released version 0.4 (unware about date) + +07/10/97 released version 0.5 + +10/09/97 released 0.6 + +10/12/97 released 0.6.1 (bugfix release) + +see ChangeLog for details + diff --git a/kreversi/Position.cpp b/kreversi/Position.cpp new file mode 100644 index 00000000..22ffb3cf --- /dev/null +++ b/kreversi/Position.cpp @@ -0,0 +1,366 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni <mweilguni@sime.com>. This file + * is ported from Mats Luthman's <Mats.Luthman@sylog.se> JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Position is used to represent an Othello position as white and +// black pieces and empty squares (see class Score) on an 8x8 Othello board. + + +#include "kdebug.h" + +#include "Position.h" +#include <stdlib.h> + + +Position::Position() +{ + setupStartPosition(); +} + + +Position::Position(Position &pos, SimpleMove &move) +{ + constrCopy(pos, move); +} + + +Position &Position::operator=(Position &pos) +{ + // Copy the position itself. + for (uint row = 0; row < 10; row++) + for (uint col = 0; col < 10; col++) + m_board[row][col] = pos.m_board[row][col]; + m_toMove = pos.m_toMove; + + m_score = pos.m_score; + + return *this; +} + + +// ---------------------------------------------------------------- +// Helpers to the constructors + + +// Construct a Position by copying another one and then make a move. +// + +void Position::constrCopy(Position &pos, SimpleMove &move) +{ + *this = pos; + doMove(move); +} + + +// Setup the Position by setting it to the initial Reversi position. + +void Position::setupStartPosition() +{ + // Initialize the real board + for (uint i = 0; i < 10; i++) + for (uint j = 0; j < 10; j++) + m_board[i][j] = Nobody; + + // The initial position + m_board[4][4] = White; + m_board[5][5] = White; + m_board[5][4] = Black; + m_board[4][5] = Black; + + // Black always starts the game in Reversi. + m_toMove = Black; + + // Each side starts out with two pieces. + m_score.set(White, 2); + m_score.set(Black, 2); +} + + +// ---------------------------------------------------------------- +// Access methods + + +Color Position::color(uint x, uint y) const +{ + if (x < 1 || x > 8 || y < 1 || y > 8) + return Nobody; + + return m_board[x][y]; +} + + +// ---------------------------------------------------------------- +// Moves in the position + + +// Return true if the move is legal. +// +// NOTE: This function does *NOT* test wether the move is done +// by the color to move. That must be checked separately. +// + +bool Position::moveIsLegal(SimpleMove &move) const +{ + if (m_board[move.x()][move.y()] != Nobody) + return false; + + Color color = move.color(); + Color opponent = ::opponent(color); + + // Check in all directions and see if there is a turnable row of + // opponent pieces. If there is at least one such row, then the + // move is legal. + for (int xinc = -1; xinc <= 1; xinc++) { + for (int yinc = -1; yinc <= 1; yinc++) { + int x, y; + + if (xinc == 0 && yinc == 0) + continue; + + // Find the end of such a row of pieces. + for (x = move.x()+xinc, y = move.y()+yinc; m_board[x][y] == opponent; + x += xinc, y += yinc) + ; + + // If the row ends with a piece of our own and there was at + // least one opponent piece between it and the move point, then + // we have found a turnable row. + if (m_board[x][y] == color + && (x - xinc != move.x() || y - yinc != move.y())) + return true; + } + } + + return false; +} + + +// See if 'color' can make a move in the current position. This is +// independent of wether it is 'color's turn to move or not. + +bool Position::moveIsPossible(Color color) const +{ + // Make it simple: Step through all squares and see if it is a legal move. + for (uint i = 1; i < 9; i++) + for (uint j = 1; j < 9; j++) { + SimpleMove move(color, i, j); + + if (moveIsLegal(move)) + return true; + } + + return false; +} + + +// Return true if any side can move. (If not, the game is over.) + +bool Position::moveIsAtAllPossible() const +{ + return (moveIsPossible(White) || moveIsPossible(Black)); +} + + +// Make a move in the position. +// +// Return true if the move was legal, otherwise return false. +// +bool Position::doMove(SimpleMove &move, QValueList<char> *turned) +{ + if (move.color() == Nobody) + return false; + + Color color = move.color(); + Color opponent = ::opponent(color); + + // Put the piece on the board + m_board[move.x()][move.y()] = color; + m_score.inc(color); + + // Turn pieces. + uint scoreBefore = m_score.score(color); + for (int xinc = -1; xinc <= 1; xinc++) { + for (int yinc = -1; yinc <= 1; yinc++) { + int x, y; + + // Skip the case where both xinc and yinc == 0, since then we + // won't move in any direction at all. + if (xinc == 0 && yinc == 0) + continue; + + // Find the end point (x, y) of a possible row of turnable pieces. + for (x = move.x()+xinc, y = move.y()+yinc; m_board[x][y] == opponent; + x += xinc, y += yinc) + ; + + // If the row was indeed turnable, then do it. + if (m_board[x][y] == color) { + for (x -= xinc, y -= yinc; x != move.x() || y != move.y(); + x -= xinc, y -= yinc) { + // Turn the piece. + m_board[x][y] = color; + if (turned) + turned->append(10 * x + y); + + // Make the piece count correct again. + m_score.inc(color); + m_score.dec(opponent); + } + } + } + } + + // If nothing was turned, the move wasn't legal. + if (m_score.score(color) == scoreBefore) { + m_board[move.x()][move.y()] = Nobody; + m_score.dec(color); + + return false; + } + + // Set the next color to move. + if (moveIsPossible(opponent)) + m_toMove = opponent; + else if (moveIsPossible(color)) + m_toMove = color; + else + m_toMove = Nobody; + + return true; +} + + +bool Position::doMove(Move &move) +{ + move.m_turnedPieces.clear(); + return doMove((SimpleMove &) move, &move.m_turnedPieces); +} + + +bool Position::undoMove(Move &move) +{ + Color color = move.color(); + Color other = opponent(color); + + // Sanity checks + // 1. The move must be on the board and be of the right color. + if (color != m_board[move.x()][move.y()]) { + //kdDebug() << "move on the board is wrong color: " << (int) color << "[" + // << move.x() << "," << move.y() << "]" << endl; + return false; + } + + // 2. All turned pieces must be on the board anb be of the right color. + QValueList<char>::iterator it; + for (it = move.m_turnedPieces.begin(); + it != move.m_turnedPieces.end(); + ++it) { + int sq = *it; + + if (m_board[sq / 10][sq % 10] != color) { + //kdDebug() << "turned piece the board is wrong color: [" + // << sq / 10 << "," << sq % 10 << "]" << endl; + return false; + } + } + + // Ok, everything seems allright. Let's do it! + // 1. Unturn all the turned pieces. + for (it = move.m_turnedPieces.begin(); + it != move.m_turnedPieces.end(); + ++it) { + int sq = *it; + + m_board[sq / 10][sq % 10] = other; + m_score.dec(color); + m_score.inc(other); + } + + // 2. Remove the move itself. + m_score.dec(color); + m_board[move.x()][move.y()] = Nobody; + + + return true; +} + + +MoveList Position::generateMoves(Color color) const +{ + MoveList moves; + + // Make it simple: Step through all squares and see if it is a legal move. + for (uint i = 1; i < 9; i++) { + for (uint j = 1; j < 9; j++) { + Move move(color, i, j); + + if (moveIsLegal(move)) + moves.append(move); + } + } + + return moves; +} + + +QString Position::asString() const +{ + QString result; + + for (uint y = 1; y < 9; ++y) { + for (uint x = 1; x < 9; ++x) { + switch (m_board[x][y]) { + case Nobody: result.append(' '); break; + case Black: result.append('*'); break; + case White: result.append('o'); break; + default: result.append('?'); break; + } + } + + result.append('\n'); + } + + return result; +} diff --git a/kreversi/Position.h b/kreversi/Position.h new file mode 100644 index 00000000..7269c2e6 --- /dev/null +++ b/kreversi/Position.h @@ -0,0 +1,98 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni <mweilguni@sime.com>. This file + * is ported from Mats Luthman's <Mats.Luthman@sylog.se> JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Position is used to represent an Othello position as white and +// black pieces and empty squares (see class Score) on an 8x8 Othello board. + +#ifndef __POSITION__H__ +#define __POSITION__H__ + + +#include "Move.h" +#include "Score.h" + + +class Position +{ +public: + Position(); + Position(Position &pos, SimpleMove &move); + Position(Position &pos, Move &move); + + Position &operator=(Position &pos); + + void constrCopy(Position &pos, SimpleMove &move); + + void setupStartPosition(); + + // Access methods + Color toMove() const { return m_toMove; } + Color color(uint x, uint y) const; + uint score(Color color) const { return m_score.score(color); } + + // Moves in the current position. + bool moveIsPossible(Color color) const; + bool moveIsAtAllPossible() const; + bool moveIsLegal(SimpleMove &move) const; + bool doMove(SimpleMove &move, QValueList<char> *turned = 0); + bool doMove(Move &move); + bool undoMove(Move &move); + + MoveList generateMoves(Color color) const; + + QString asString() const; + +private: + // The actual position itself. Use the simplest representation possible. + Color m_board[10][10]; + Color m_toMove; + + // Some extra data + Score m_score; // The number of pieces for each side. +}; + + +#endif diff --git a/kreversi/Score.cpp b/kreversi/Score.cpp new file mode 100644 index 00000000..f508152b --- /dev/null +++ b/kreversi/Score.cpp @@ -0,0 +1,70 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni <mweilguni@sime.com>. This file + * is ported from Mats Luthman's <Mats.Luthman@sylog.se> JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include "Score.h" + + +Score::Score() +{ + m_score[White] = 0; + m_score[Black] = 0; +} + + +/* Return the opponent color for 'color'. + */ + +Color opponent(Color color) +{ + switch (color) { + case White: return Black; + case Black: return White; + case Nobody: break; + } + + return Nobody; +} diff --git a/kreversi/Score.h b/kreversi/Score.h new file mode 100644 index 00000000..947272dc --- /dev/null +++ b/kreversi/Score.h @@ -0,0 +1,78 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni <mweilguni@sime.com>. This file + * is ported from Mats Luthman's <Mats.Luthman@sylog.se> JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#ifndef __SCORE__H__ +#define __SCORE__H__ + +#include <qglobal.h> + +enum Color { White = 0, Black = 1, NbColors = 2, Nobody = NbColors }; + +Color opponent(Color color); + + +/* This class keeps track of the score for both colors. Such a score + * could be either the number of pieces, the score from the evaluation + * function or anything similar. + */ + +class Score { +public: + Score(); + + uint score(Color color) const { return m_score[color]; } + + void set(Color color, uint score) { m_score[color] = score; } + void inc(Color color) { m_score[color]++; } + void dec(Color color) { m_score[color]--; } + void add(Color color, uint s) { m_score[color] += s; } + void sub(Color color, uint s) { m_score[color] -= s; } + +private: + uint m_score[NbColors]; +}; + +#endif diff --git a/kreversi/SuperEngine.cpp b/kreversi/SuperEngine.cpp new file mode 100644 index 00000000..3da7326f --- /dev/null +++ b/kreversi/SuperEngine.cpp @@ -0,0 +1,133 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni <mweilguni@sime.com>. This file + * is ported from Mats Luthman's <Mats.Luthman@sylog.se> JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class SuperEngine is a super class for engines that produce moves. +// It implements functionality that move engines have in common, which is +// useful if you want to use several different engines in the same program +// (for instance when you are test playing different strategies against each +// other). +// +// SuperEngine implements: +// +// Random number handling (engines should not play exactly the same games +// repeatedly). +// +// Setting playing strength level. +// +// Functionality for telling the engine to interrupt calculation. +// + +// Public and protected functions: + +// public SuperEngine(int st) +// Creates an engine playing at level st. All integers greater than 0 +// should be possible levels. There need not be any actual difference in +// playing strength between levels, but if there is, higher number should +// mean greater playing strength. + +// public SuperEngine(int st, int sd) +// The same as above, but uses sd as the seed for the random generator. +// This means that the engine always behaves in exactly the same way +// (practical when testing). + +// public synchronized final void SetInterrupt(boolean intr) +// This function could be called when ComputeMove() (see below) is +// executing. It tells the engine to interrupt calculation as +// soon as possible and return null from ComputeMove(). + +// protected synchronized final boolean GetInterrupt() +// Returns true when SetInterrupt() has been called. Should be called +// with short intervals from ComputeMove(). + +// public void SetStrength(int st) +// Sets playing strength level. + +// public int GetStrength() +// Gets playing strength level. + +// public void SetSeed(int sd) +// Changes the random seed. + +// public abstract Move ComputeMove(Game g) +// This function should produce a move. If SetInterrupt() is called +// during its execution it should return null as soon as possible. + +// Protected members: + +// protected int m_strength +// Is set and read by SetStrength() and GetStrength(). + +// protected Random m_random +// Could (and should in a good engine) be used to prevent the engine from +// repeating itself, always playing the same moves in the same positions. +// If this is not done, winning once would make it possible to play the +// same moves and win every time against the program. + + +#include "SuperEngine.h" + + +SuperEngine::SuperEngine(int st) +{ + m_strength = st; + m_random.setSeed(0); + m_interrupt = false; +} + + +SuperEngine::SuperEngine(int st, int sd) +{ + m_strength = st; + m_random.setSeed(sd); + m_interrupt = false; +} + + +void SuperEngine::setSeed(int sd) +{ + m_random.setSeed(sd); +} + diff --git a/kreversi/SuperEngine.h b/kreversi/SuperEngine.h new file mode 100644 index 00000000..d3faf6a9 --- /dev/null +++ b/kreversi/SuperEngine.h @@ -0,0 +1,142 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni <mweilguni@sime.com>. This file + * is ported from Mats Luthman's <Mats.Luthman@sylog.se> JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class SuperEngine is a super class for engines that produce moves. +// It implements functionality that move engines have in common, which is +// useful if you want to use several different engines in the same program +// (for instance when you are test playing different strategies against each +// other). +// +// SuperEngine implements: +// +// Random number handling (engines should not play exactly the same games +// repeatedly). +// +// Setting playing strength level. +// +// Functionality for telling the engine to interrupt calculation. +// + +// Public and protected functions: + +// public SuperEngine(int st) +// Creates an engine playing at level st. All integers greater than 0 +// should be possible levels. There need not be any actual difference in +// playing strength between levels, but if there is, higher number should +// mean greater playing strength. + +// public SuperEngine(int st, int sd) +// The same as above, but uses sd as the seed for the random generator. +// This means that the engine always behaves in exactly the same way +// (practical when testing). + +// public synchronized final void SetInterrupt(boolean intr) +// This function could be called when ComputeMove() (see below) is +// executing. It tells the engine to interrupt calculation as +// soon as possible and return null from ComputeMove(). + +// protected synchronized final boolean GetInterrupt() +// Returns true when SetInterrupt() has been called. Should be called +// with short intervals from ComputeMove(). + +// public void SetStrength(int st) +// Sets playing strength level. + +// public int GetStrength() +// Gets playing strength level. + +// public void SetSeed(int sd) +// Changes the random seed. + +// public abstract Move ComputeMove(Game g) +// This function should produce a move. If SetInterrupt() is called +// during its execution it should return null as soon as possible. + +// Protected members: + +// protected int m_strength +// Is set and read by SetStrength() and GetStrength(). + +// protected Random m_random +// Could (and should in a good engine) be used to prevent the engine from +// repeating itself, always playing the same moves in the same positions. +// If this is not done, winning once would make it possible to play the +// same moves and win every time against the program. + +#ifndef __SUPERENGINE__H__ +#define __SUPERENGINE__H__ + +#include "Game.h" + +#include <krandomsequence.h> + +class SuperEngine { +public: + SuperEngine(int st); + SuperEngine(int st, int sd); + virtual ~SuperEngine() {} + + void setInterrupt(bool intr) { m_interrupt = intr; } + bool interrupted() const { return m_interrupt; } + + enum Strength { MinStrength = 1, MaxStrength = 7, + NbStrengths = MaxStrength }; + void setStrength(uint st) { m_strength = st; } + uint strength() const { return m_strength; } + + void setSeed(int sd); + + virtual Move computeMove(Game *game, bool competitive) = 0; + +protected: + uint m_strength; + KRandomSequence m_random; + +private: + bool m_interrupt; +}; + +#endif diff --git a/kreversi/TODO b/kreversi/TODO new file mode 100644 index 00000000..4e81f5d1 --- /dev/null +++ b/kreversi/TODO @@ -0,0 +1,87 @@ +TODO-list for KREVERSI +====================== + +Next +---- + +* + + +================================================================ + + +* Implement the plans in DESIGN + + Implement the QReversiGameView class DONE + - Move board view to it done + - Move movelist to it done + - Move status widgets to it done + + Move all showing of legal moves into the BoardView class DONE + + Implement the QEngineView class ---- + +* More cleaning / refactoring + + class KReversi is still a bit of a mess. Separate it more DONE + +* Enhancements to the view + + Letters A-H and figures 1-8 on the board view. DONE + + + Show possible moves in the current position DONE + - Actually show them on the board done + - Create a toggle action to toggle it on/off done + - Make an icon for the toggle action -- + - Bug: legal moves don't get updated if one side has to pass done + - Bug: legal moves don't work together with hint. done + + Show moves made during the game DONE + + + Navigate in the list of moves ---- + + + Wish 102813: Should be able to show last move DONE + - Make an icon for the toggle action -- + - Bug: When turned on, should show last move immediately done + - Bug: When turned off, should unshow last move immediately done + + + Save settings of toggleactions in config file. ---- + +* Convert KReversi to use KGame / KPlayer + I. Convert KReversi to a proper Model/View program. + 1. Fix a ReversiGame (formerly known as Game) DONE + - Clean it up. (Only store the moves). + - Add a few necessary methods. + 2. Move all the slots for KActions to kreversi.cpp DONE + 3. Move the ownership of the engine and the game to kreversi. DONE + 4. Create a new class QReversiGame, that inherits ReversiGame DONE + and sends a lot of signals. + - Split out a lot of methods from the current class Board. done + 5. Create QReversiBoardView from the rest of the current Board DONE + - Clean it done + + II. Introduce a class ReversiPlayer + + III. Convert everything to KGame + 1. Let KReversiGame inherit from KGame + 2. Let ReversiPlayer inherit from KPlayer. + + IV. ... + + V. Profit! + + + + +Old TODO items, partially done/not done +======================================= + +* undo/redo + undo works, but I'll probably do not make a redo function + +* Sound support: + I'm not happy with + the sound files I have so if + you have better sounds, mail them to me + (uuencoded). I need sounds for the following actions: + - game won + - game lost + - game drawn + - turning a piece + - putting a piece + - something for the hall of fame (trumpets???) + diff --git a/kreversi/board.cpp b/kreversi/board.cpp new file mode 100644 index 00000000..9d367a38 --- /dev/null +++ b/kreversi/board.cpp @@ -0,0 +1,576 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni <mweilguni@sime.com> + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include <unistd.h> + +#include <qpainter.h> +#include <qfont.h> + +#include <kapplication.h> +#include <kstandarddirs.h> +#include <kconfig.h> +#include <knotifyclient.h> +#include <klocale.h> +#include <kexthighscore.h> +#include <kdebug.h> + +#include "board.h" +#include "prefs.h" +#include "Engine.h" +#include "qreversigame.h" + + +#ifndef PICDATA +#define PICDATA(x) KGlobal::dirs()->findResource("appdata", QString("pics/")+ x) +#endif + +const uint HINT_BLINKRATE = 250000; +const uint ANIMATION_DELAY = 3000; +const uint CHIP_OFFSET[NbColors] = { 24, 1 }; +const uint CHIP_SIZE = 36; + +#define OFFSET() (zoomedSize() * 3 / 4) + + +// ================================================================ +// class KReversiBoardView + + +QReversiBoardView::QReversiBoardView(QWidget *parent, QReversiGame *krgame) + : QWidget(parent, "board"), + chiptype(Unloaded) +{ + m_krgame = krgame; + + m_marksShowing = true; + m_legalMovesShowing = false; + m_legalMoves.clear(); + + m_showLastMove = false; + m_lastMoveShown = SimpleMove(); +} + + +QReversiBoardView::~QReversiBoardView() +{ +} + + +// ---------------------------------------------------------------- + + +// Start it all up. +// + +void QReversiBoardView::start() +{ + updateBoard(true); + adjustSize(); +} + + +void QReversiBoardView::loadChips(ChipType type) +{ + QString name("pics/"); + name += (type==Colored ? "chips.png" : "chips_mono.png"); + + QString s = KGlobal::dirs()->findResource("appdata", name); + bool ok = allchips.load(s); + + Q_ASSERT( ok && allchips.width()==CHIP_SIZE*5 + && allchips.height()==CHIP_SIZE*5 ); + chiptype = type; + update(); +} + + +// Negative speed is allowed. If speed is negative, +// no animations are displayed. +// + +void QReversiBoardView::setAnimationSpeed(uint speed) +{ + if (speed <= 10) + anim_speed = speed; +} + + +// Handle mouse clicks. +// + +void QReversiBoardView::mousePressEvent(QMouseEvent *e) +{ + // Only handle left button. No context menu. + if ( e->button() != LeftButton ) { + e->ignore(); + return; + } + + int offset = m_marksShowing ? OFFSET() : 0; + int px = e->pos().x()- 1 - offset; + int py = e->pos().y()- 1 - offset; + + if (px < 0 || px >= 8 * (int) zoomedSize() + || py < 0 || py >= 8 * (int) zoomedSize()) { + e->ignore(); + return; + } + + emit signalSquareClicked(py / zoomedSize(), px / zoomedSize()); +} + + +void QReversiBoardView::showHint(Move move) +{ + // Only show a hint if there is a move to show. + if (move.x() == -1) + return; + + // Blink with a piece at the location where the hint move points. + // + // The isVisible condition has been added so that when the player + // was viewing a hint and quits the game window, the game doesn't + // still have to do all this looping and directly ends. + QPainter p(this); + p.setPen(black); + m_hintShowing = true; + for (int flash = 0; + flash < 100 && m_hintShowing && isVisible(); + flash++) + { + if (flash & 1) { + // FIXME: Draw small circle if showLegalMoves is turned on. + drawPiece(move.y() - 1, move.x() - 1, Nobody); + if (m_legalMovesShowing) + drawSmallCircle(move.x(), move.y(), p); + } + else + drawPiece(move.y() - 1, move.x() - 1, m_krgame->toMove()); + + // keep GUI alive while waiting + for (int dummy = 0; dummy < 5; dummy++) { + usleep(HINT_BLINKRATE / 5); + qApp->processEvents(); + } + } + m_hintShowing = false; + + // Draw the empty square again. + drawPiece(move.y() - 1, move.x() - 1, m_krgame->color(move.x(), move.y())); + if (m_legalMovesShowing) + drawSmallCircle(move.x(), move.y(), p); +} + + +// Set the member m_hintShowing to false. This will make showHint() +// end, if it is running. +// + +void QReversiBoardView::quitHint() +{ + m_hintShowing = false; +} + + +void QReversiBoardView::setShowLegalMoves(bool show) +{ + m_legalMovesShowing = show; + updateBoard(true); +} + +void QReversiBoardView::setShowMarks(bool show) +{ + m_marksShowing = show; + updateBoard(true); +} + + +void QReversiBoardView::setShowLastMove(bool show) +{ + m_showLastMove = show; + updateBoard(true); +} + + +// ================================================================ +// Functions related to drawing/painting + + +// Flash all pieces which are turned. +// +// NOTE: This code is quite a hack. Should make it better. +// + +void QReversiBoardView::animateChanged(Move move) +{ + if (anim_speed == 0) + return; + + // Draw the new piece. + drawPiece(move.y() - 1, move.x() - 1, move.color()); + + // Animate row by row in all directions. + for (int dx = -1; dx < 2; dx++) + for (int dy = -1; dy < 2; dy++) + if ((dx != 0) || (dy != 0)) + animateChangedRow(move.y() - 1, move.x() - 1, dy, dx); +} + + +bool QReversiBoardView::isField(int row, int col) const +{ + return ((0 <= row) && (row < 8) && (0 <= col) && (col < 8)); +} + + +void QReversiBoardView::animateChangedRow(int row, int col, int dy, int dx) +{ + row = row + dy; + col = col + dx; + while (isField(row, col)) { + if (m_krgame->wasTurned(col+1, row+1)) { + KNotifyClient::event(winId(), "click", i18n("Click")); + rotateChip(row, col); + } else + return; + + col += dx; + row += dy; + } +} + + +void QReversiBoardView::rotateChip(uint row, uint col) +{ + // Check which direction the chip has to be rotated. If the new + // chip is white, the chip was black first, so lets begin at index + // 1, otherwise it was white. + Color color = m_krgame->color(col+1, row+1); + uint from = CHIP_OFFSET[opponent(color)]; + uint end = CHIP_OFFSET[color]; + int delta = (color==White ? 1 : -1); + + from += delta; + end -= delta; + + for (uint i = from; i != end; i += delta) { + drawOnePiece(row, col, i); + kapp->flushX(); // FIXME: use QCanvas to avoid flicker... + usleep(ANIMATION_DELAY * anim_speed); + } +} + + +// Redraw the board. If 'force' is true, redraw everything, otherwise +// only redraw those squares that have changed (marked by +// m_krgame->squareModified(col, row)). +// + +void QReversiBoardView::updateBoard (bool force) +{ + QPainter p(this); + p.setPen(black); + + // If we are showing legal moves, we have to erase the old ones + // before we can show the new ones. The easiest way to do that is + // to repaint everything. + // + // FIXME: A better way, perhaps, is to do the repainting in + // drawPiece (which should be renamed drawSquare). + if (m_legalMovesShowing) + force = true; + + // Draw the squares of the board. + for (uint row = 0; row < 8; row++) + for (uint col = 0; col < 8; col++) + if ( force || m_krgame->squareModified(col + 1, row + 1) ) { + Color color = m_krgame->color(col + 1, row + 1); + drawPiece(row, col, color); + } + + // Draw a border around the board. + int offset = m_marksShowing ? OFFSET() : 0; + p.drawRect(0 + offset, 0 + offset, + 8 * zoomedSize() + 2, 8 * zoomedSize() + 2); + + // Draw letters and numbers if appropriate. + if (m_marksShowing) { + QFont font("Sans Serif", zoomedSize() / 2 - 6); + font.setWeight(QFont::DemiBold); + QFontMetrics metrics(font); + + p.setFont(font); + uint charHeight = metrics.ascent(); + for (uint i = 0; i < 8; i++) { + QChar letter = "ABCDEFGH"[i]; + QChar number = "12345678"[i]; + uint charWidth = metrics.charWidth("ABCDEFGH", i); + + // The horizontal letters + p.drawText(offset + i * zoomedSize() + (zoomedSize() - charWidth) / 2, + offset - charHeight / 2 + 2, + QString(letter)); + p.drawText(offset + i * zoomedSize() + (zoomedSize() - charWidth) / 2, + offset + 8 * zoomedSize() + offset - charHeight / 2 + 2, + QString(letter)); + + // The vertical numbers + p.drawText((offset - charWidth) / 2 + 2, + offset + (i + 1) * zoomedSize() - charHeight / 2 + 2, + QString(number)); + p.drawText(offset + 8 * zoomedSize() + (offset - charWidth) / 2 + 2, + offset + (i + 1) * zoomedSize() - charHeight / 2 + 2, + QString(number)); + } + } + + // Show legal moves. + if (m_legalMovesShowing) + showLegalMoves(); + + // Show last move + int ellipseSize = zoomedSize() / 3; + SimpleMove lastMove = m_krgame->lastMove(); + if (m_showLastMove && lastMove.x() != -1) { + // Remove the last shown last move. + int col = m_lastMoveShown.x(); + int row = m_lastMoveShown.y(); + if (col != -1 && row != -1) { + if (lastMove.x() != col || lastMove.y() != row) { + //kdDebug() << "Redrawing piece at [" << col << "," << row + // << "] with color " << m_krgame->color(col, row) + // << endl; + drawPiece(row - 1, col - 1, m_krgame->color(col, row)); + } + } + + p.setPen(yellow); + p.setBackgroundColor(yellow); + p.setBrush(SolidPattern); + + //kdDebug() << "Marking last move at [" + // << lastMove.x() << "," << lastMove.y() << "]" + // << endl; + int px = offset + (lastMove.x() - 1) * zoomedSize() + zoomedSize() / 2; + int py = offset + (lastMove.y() - 1) * zoomedSize() + zoomedSize() / 2; + p.drawEllipse(px - ellipseSize / 2 + 1, py - ellipseSize / 2 + 1, + ellipseSize - 1, ellipseSize - 1); + + m_lastMoveShown = lastMove; + + p.setPen(black); + p.setBackgroundColor(black); + p.setBrush(NoBrush); + } +} + + +// Show legal moves on the board. + +void QReversiBoardView::showLegalMoves() +{ + QPainter p(this); + p.setPen(black); + + // Get the legal moves in the current position. + Color toMove = m_krgame->toMove(); + MoveList moves = m_krgame->position().generateMoves(toMove); + + // Show the moves on the board. + MoveList::iterator it; + for (it = moves.begin(); it != moves.end(); ++it) + drawSmallCircle((*it).x(), (*it).y(), p); +} + + +void QReversiBoardView::drawSmallCircle(int x, int y, QPainter &p) +{ + int offset = m_marksShowing ? OFFSET() : 0; + int ellipseSize = zoomedSize() / 3; + + int px = offset + (x - 1) * zoomedSize() + zoomedSize() / 2; + int py = offset + (y - 1) * zoomedSize() + zoomedSize() / 2; + + p.drawEllipse(px - ellipseSize / 2, py - ellipseSize / 2, + ellipseSize, ellipseSize); +} + + + +QPixmap QReversiBoardView::chipPixmap(Color color, uint size) const +{ + return chipPixmap(CHIP_OFFSET[color], size); +} + + +// Get a pixmap for the chip 'i' at size 'size'. +// + +QPixmap QReversiBoardView::chipPixmap(uint i, uint size) const +{ + // Get the part of the 'allchips' pixmap that contains exactly that + // chip that we want to use. + QPixmap pix(CHIP_SIZE, CHIP_SIZE); + copyBlt(&pix, 0, 0, &allchips, (i%5) * CHIP_SIZE, (i/5) * CHIP_SIZE, + CHIP_SIZE, CHIP_SIZE); + + // Resize (scale) the pixmap to the desired size. + QWMatrix wm3; + wm3.scale(float(size)/CHIP_SIZE, float(size)/CHIP_SIZE); + + return pix.xForm(wm3); +} + + +uint QReversiBoardView::zoomedSize() const +{ + return qRound(float(CHIP_SIZE) * Prefs::zoom() / 100); +} + + +void QReversiBoardView::drawPiece(uint row, uint col, Color color) +{ + int i = (color == Nobody ? -1 : int(CHIP_OFFSET[color])); + drawOnePiece(row, col, i); +} + + +void QReversiBoardView::drawOnePiece(uint row, uint col, int i) +{ + int px = col * zoomedSize() + 1; + int py = row * zoomedSize() + 1; + QPainter p(this); + + // Draw either a background pixmap or a background color to the square. + int offset = m_marksShowing ? OFFSET() : 0; + if (bg.width()) + p.drawTiledPixmap(px + offset, py + offset, + zoomedSize(), zoomedSize(), bg, px, py); + else + p.fillRect(px + offset, py + offset, + zoomedSize(), zoomedSize(), bgColor); + + // Draw a black border around the square. + p.setPen(black); + p.drawRect(px + offset, py + offset, zoomedSize(), zoomedSize()); + + // If no piece on the square, i.e. only the background, then return here... + if ( i == -1 ) + return; + + // ...otherwise finally draw the piece on the square. + p.drawPixmap(px + offset, py + offset, chipPixmap(i, zoomedSize())); +} + + +// We got a repaint event. We make it easy for us and redraw the +// entire board. +// + +void QReversiBoardView::paintEvent(QPaintEvent *) +{ + updateBoard(true); +} + + +void QReversiBoardView::adjustSize() +{ + int w = 8 * zoomedSize(); + + if (m_marksShowing) + w += 2 * OFFSET(); + + setFixedSize(w + 2, w + 2); +} + + +void QReversiBoardView::setPixmap(QPixmap &pm) +{ + if ( pm.width() == 0 ) + return; + + bg = pm; + update(); + setErasePixmap(pm); +} + + +void QReversiBoardView::setColor(const QColor &c) +{ + bgColor = c; + bg = QPixmap(); + update(); + setEraseColor(c); +} + + +// Load all settings that have to do with the board view, such as +// piece colors, background, animation speed, an so on. + +void QReversiBoardView::loadSettings() +{ + // Colors of the pieces (red/blue or black/white) + if ( Prefs::grayscale() ) { + if (chiptype != Grayscale) + loadChips(Grayscale); + } + else { + if (chiptype != Colored) + loadChips(Colored); + } + + // Animation speed. + if ( !Prefs::animation() ) + setAnimationSpeed(0); + else + setAnimationSpeed(10 - Prefs::animationSpeed()); + + // Background + if ( Prefs::backgroundImageChoice() ) { + QPixmap pm( Prefs::backgroundImage() ); + if (!pm.isNull()) + setPixmap(pm); + } else { + setColor( Prefs::backgroundColor() ); + } +} + + +#include "board.moc" + diff --git a/kreversi/board.h b/kreversi/board.h new file mode 100644 index 00000000..4f9d1603 --- /dev/null +++ b/kreversi/board.h @@ -0,0 +1,150 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni <mweilguni@sime.com> + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#ifndef __BOARD__H__ +#define __BOARD__H__ + +#include <qwidget.h> +#include <qpixmap.h> + +#include "Position.h" +//#include "Game.h" +#include "Move.h" + + +class KConfig; + +class QReversiGame; + +// The class Board is the visible Reversi Board widget. +// + +class QReversiBoardView : public QWidget { + Q_OBJECT + +public: + + QReversiBoardView(QWidget *parent, QReversiGame *game); + ~QReversiBoardView(); + + // starts all: emits some signal, so it can't be called from + // constructor + void start(); + + // Used by the outer KZoomMainWindow class. + void adjustSize(); + + // Show a hint to the user. + void showHint(Move move); + void quitHint(); + + // Turn on or off some special features. + void setShowLegalMoves(bool show); + void setShowMarks(bool show); + void setShowLastMove(bool show); + + // View methods called from the outside. + void updateBoard(bool force = FALSE); + void animateChanged(Move move); + void setAnimationSpeed(uint); + + void loadSettings(); + + // To get the pixmap for the status view + QPixmap chipPixmap(Color color, uint size) const; + + +signals: + void signalSquareClicked(int, int); + + +protected: + + // event stuff + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *); + + +private: + uint zoomedSize() const; + void drawPiece(uint row, uint col, Color); + void drawOnePiece(uint row, uint col, int i); + void animateChangedRow(int row, int col, int dy, int dx); + void rotateChip(uint row, uint col); + bool isField(int row, int col) const; + + void setColor(const QColor &); + QColor color() const { return bgColor; } + void setPixmap(QPixmap &); + + // Methods for handling images of pieces. + enum ChipType { Unloaded, Colored, Grayscale }; + void loadChips(ChipType); + ChipType chipType() const { return chiptype; } + QPixmap chipPixmap(uint i, uint size) const; + + // Private drawing methods. + void showLegalMoves(); + void drawSmallCircle(int x, int y, QPainter &p); + + +private: + QReversiGame *m_krgame; // Pointer to the game object (not owner). + + // The background of the board - a color and a pixmap. + QColor bgColor; + QPixmap bg; + + // the pieces + ChipType chiptype; + QPixmap allchips; + uint anim_speed; + + // Special stuff used only in smaller areas. + bool m_hintShowing; + MoveList m_legalMoves; + bool m_legalMovesShowing; + bool m_marksShowing; + + bool m_showLastMove; + SimpleMove m_lastMoveShown; +}; + + +#endif + diff --git a/kreversi/highscores.cpp b/kreversi/highscores.cpp new file mode 100644 index 00000000..88317a87 --- /dev/null +++ b/kreversi/highscores.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2004 Nicolas HADACEK (hadacek@kde.org) + * + * 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 "highscores.h" + +#include <klocale.h> +#include <kconfig.h> +#include <kapplication.h> + + +namespace KExtHighscore +{ + +const ExtManager::Data ExtManager::DATA[SuperEngine::NbStrengths] = { + { I18N_NOOP("1 (Beginner)"), "beginner" }, + { I18N_NOOP("2"), 0 }, + { I18N_NOOP("3"), 0 }, + { I18N_NOOP("4 (Average)"), "average" }, + { I18N_NOOP("5"), 0 }, + { I18N_NOOP("6"), 0 }, + { I18N_NOOP("7 (Expert)"), "expert" } +}; + + +ExtManager::ExtManager() + : Manager(SuperEngine::NbStrengths) +{ + setShowStatistics(true); + setShowDrawGamesStatistic(true); + + const uint RANGE[6] = { 0, 32, 40, 48, 56, 64 }; + QMemArray<uint> s; + s.duplicate(RANGE, 6); + setScoreHistogram(s, ScoreBound); +} + + +QString ExtManager::gameTypeLabel(uint gameType, LabelType type) const +{ + const Data &data = DATA[gameType]; + switch (type) { + case Icon: return data.icon; + case Standard: return QString::number(gameType); + case I18N: return i18n(data.label); + case WW: break; + } + + return QString::null; +} + + +void ExtManager::convertLegacy(uint gameType) +{ + // Since there is no information about the skill level + // in the legacy highscore list, consider they are + // for beginner skill ... + qDebug("convert legacy %i", gameType); + + if ( gameType!=0 ) + return; + + KConfigGroupSaver cg(kapp->config(), "High Score"); + + for (uint i = 1; i <= 10; i++) { + QString key = "Pos" + QString::number(i); + QString name = cg.config()->readEntry(key + "Name", QString::null); + + if ( name.isEmpty() ) + name = i18n("anonymous"); + + uint score = cg.config()->readUnsignedNumEntry(key + "NumChips", 0); + if ( score==0 ) + continue; + + QString sdate = cg.config()->readEntry(key + "Date", QString::null); + QDateTime date = QDateTime::fromString(sdate); + Score s(Won); + + s.setScore(score); + s.setData("name", name); + if ( date.isValid() ) + s.setData("date", date); + submitLegacyScore(s); + } +} + + +} // Namespace diff --git a/kreversi/highscores.h b/kreversi/highscores.h new file mode 100644 index 00000000..a599d12d --- /dev/null +++ b/kreversi/highscores.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2004 Nicolas HADACEK (hadacek@kde.org) + * + * 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. + */ + + +#ifndef HIGHSCORES_H +#define HIGHSCORES_H + + +#include <kexthighscore.h> +#include <kdemacros.h> +#include "SuperEngine.h" + + +namespace KExtHighscore +{ + +class KDE_EXPORT ExtManager : public Manager +{ + public: + ExtManager(); + + private: + QString gameTypeLabel(uint gameTye, LabelType) const; + void convertLegacy(uint gameType); + + struct Data { + const char *label; + const char *icon; + }; + static const Data DATA[SuperEngine::NbStrengths]; +}; + +} + +#endif diff --git a/kreversi/icons/Makefile.am b/kreversi/icons/Makefile.am new file mode 100644 index 00000000..da1319dc --- /dev/null +++ b/kreversi/icons/Makefile.am @@ -0,0 +1 @@ +KDE_ICON = AUTO
\ No newline at end of file diff --git a/kreversi/icons/cr16-action-lastmoves.png b/kreversi/icons/cr16-action-lastmoves.png Binary files differnew file mode 100644 index 00000000..5543a104 --- /dev/null +++ b/kreversi/icons/cr16-action-lastmoves.png diff --git a/kreversi/icons/cr16-action-legalmoves.png b/kreversi/icons/cr16-action-legalmoves.png Binary files differnew file mode 100644 index 00000000..5ef60f95 --- /dev/null +++ b/kreversi/icons/cr16-action-legalmoves.png diff --git a/kreversi/icons/cr22-action-lastmoves.png b/kreversi/icons/cr22-action-lastmoves.png Binary files differnew file mode 100644 index 00000000..29d0f962 --- /dev/null +++ b/kreversi/icons/cr22-action-lastmoves.png diff --git a/kreversi/icons/cr22-action-legalmoves.png b/kreversi/icons/cr22-action-legalmoves.png Binary files differnew file mode 100644 index 00000000..3158b678 --- /dev/null +++ b/kreversi/icons/cr22-action-legalmoves.png diff --git a/kreversi/icons/cr32-action-lastmoves.png b/kreversi/icons/cr32-action-lastmoves.png Binary files differnew file mode 100644 index 00000000..47166dd7 --- /dev/null +++ b/kreversi/icons/cr32-action-lastmoves.png diff --git a/kreversi/icons/cr32-action-legalmoves.png b/kreversi/icons/cr32-action-legalmoves.png Binary files differnew file mode 100644 index 00000000..c5a0e304 --- /dev/null +++ b/kreversi/icons/cr32-action-legalmoves.png diff --git a/kreversi/icons/cr48-action-lastmoves.png b/kreversi/icons/cr48-action-lastmoves.png Binary files differnew file mode 100644 index 00000000..47a3e0da --- /dev/null +++ b/kreversi/icons/cr48-action-lastmoves.png diff --git a/kreversi/icons/cr48-action-legalmoves.png b/kreversi/icons/cr48-action-legalmoves.png Binary files differnew file mode 100644 index 00000000..5742a01d --- /dev/null +++ b/kreversi/icons/cr48-action-legalmoves.png diff --git a/kreversi/icons/crsc-action-lastmoves.svgz b/kreversi/icons/crsc-action-lastmoves.svgz Binary files differnew file mode 100644 index 00000000..c2b1c4bb --- /dev/null +++ b/kreversi/icons/crsc-action-lastmoves.svgz diff --git a/kreversi/icons/crsc-action-legalmoves.svgz b/kreversi/icons/crsc-action-legalmoves.svgz Binary files differnew file mode 100644 index 00000000..db2acad9 --- /dev/null +++ b/kreversi/icons/crsc-action-legalmoves.svgz diff --git a/kreversi/icons/hi128-app-kreversi.png b/kreversi/icons/hi128-app-kreversi.png Binary files differnew file mode 100644 index 00000000..26c22bc8 --- /dev/null +++ b/kreversi/icons/hi128-app-kreversi.png diff --git a/kreversi/icons/hi16-app-kreversi.png b/kreversi/icons/hi16-app-kreversi.png Binary files differnew file mode 100644 index 00000000..9dda53b2 --- /dev/null +++ b/kreversi/icons/hi16-app-kreversi.png diff --git a/kreversi/icons/hi22-app-kreversi.png b/kreversi/icons/hi22-app-kreversi.png Binary files differnew file mode 100644 index 00000000..6f77f5a9 --- /dev/null +++ b/kreversi/icons/hi22-app-kreversi.png diff --git a/kreversi/icons/hi32-app-kreversi.png b/kreversi/icons/hi32-app-kreversi.png Binary files differnew file mode 100644 index 00000000..360f1bda --- /dev/null +++ b/kreversi/icons/hi32-app-kreversi.png diff --git a/kreversi/icons/hi48-app-kreversi.png b/kreversi/icons/hi48-app-kreversi.png Binary files differnew file mode 100644 index 00000000..b310d52f --- /dev/null +++ b/kreversi/icons/hi48-app-kreversi.png diff --git a/kreversi/icons/hi64-app-kreversi.png b/kreversi/icons/hi64-app-kreversi.png Binary files differnew file mode 100644 index 00000000..5ca5cb5d --- /dev/null +++ b/kreversi/icons/hi64-app-kreversi.png diff --git a/kreversi/kreversi.cpp b/kreversi/kreversi.cpp new file mode 100644 index 00000000..b0a5ddc8 --- /dev/null +++ b/kreversi/kreversi.cpp @@ -0,0 +1,841 @@ +/* Yo Emacs, this is -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni <mweilguni@sime.com> + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include <unistd.h> + +#include <qlayout.h> +#include <qlabel.h> +#include <qlistbox.h> + +#include <kapplication.h> +#include <kdebug.h> +#include <kconfig.h> +#include <kstandarddirs.h> +#include <kstatusbar.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kaction.h> +#include <kstdgameaction.h> +#include <kkeydialog.h> +#include <kconfigdialog.h> +#include <knotifyclient.h> +#include <knotifydialog.h> +#include <kexthighscore.h> + +#include "Score.h" +#include "kreversi.h" + +// Automatically generated headers +#include "prefs.h" +#include "settings.h" + +#include "kreversi.moc" + + +// ================================================================ +// class KReversi + + +#ifndef PICDATA +#define PICDATA(x) \ + KGlobal::dirs()->findResource("appdata", QString("pics/") + x) +#endif + + +KReversi::KReversi() + : KZoomMainWindow(10, 300, 5, "kreversi"), + m_gameOver(false) +{ + QWidget *w; + QGridLayout *top; + + KNotifyClient::startDaemon(); + + // The game. + m_game = new QReversiGame(); + m_cheating = false; + m_gameOver = false; + m_humanColor = Black; + + // The Engine + m_engine = new Engine(); + setStrength(1); + + // The visual stuff + w = new QWidget(this); + setCentralWidget(w); + + top = new QGridLayout(w, 2, 2); + + // The reversi game view. + m_gameView = new QReversiGameView(w, m_game); + top->addMultiCellWidget(m_gameView, 0, 1, 0, 0); + + // Populate the GUI. + createKActions(); + addWidget(m_gameView); + + // Connect the signals from the game with slots of the view + // + // The only part of the view that is left in this class is the + // indicator of whose turn it is in the status bar. The rest is + // in the game view. + connect(m_game, SIGNAL(sig_newGame()), this, SLOT(showTurn())); + connect(m_game, SIGNAL(sig_move(uint, Move&)), + this, SLOT(handleMove(uint, Move&))); // Calls showTurn(). + connect(m_game, SIGNAL(sig_update()), this, SLOT(showTurn())); + connect(m_game, SIGNAL(sig_gameOver()), this, SLOT(slotGameOver())); + + // Signal that is sent when the user clicks on the board. + connect(m_gameView, SIGNAL(signalSquareClicked(int, int)), + this, SLOT(slotSquareClicked(int, int))); + + loadSettings(); + + setupGUI(); + init("popup"); + m_gameView->start(); + + slotNewGame(); +} + + +KReversi::~KReversi() +{ + delete m_game; + delete m_engine; +} + + + +// Create all KActions used in KReversi. +// + +void KReversi::createKActions() +{ + // Standard Game Actions. + KStdGameAction::gameNew(this, SLOT(slotNewGame()), actionCollection(), + "game_new"); + KStdGameAction::load(this, SLOT(slotOpenGame()), actionCollection()); + KStdGameAction::save(this, SLOT(slotSave()), actionCollection()); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + KStdGameAction::hint(this, SLOT(slotHint()), actionCollection(), + "game_hint"); + KStdGameAction::undo(this, SLOT(slotUndo()), actionCollection(), + "game_undo"); + + // Non-standard Game Actions: Stop, Continue, Switch sides + stopAction = new KAction(i18n("&Stop Thinking"), "game_stop", Qt::Key_Escape, + this, SLOT(slotInterrupt()), actionCollection(), + "game_stop"); + continueAction = new KAction(i18n("&Continue Thinking"), "reload", 0, + this, SLOT(slotContinue()), actionCollection(), + "game_continue"); + new KAction(i18n("S&witch Sides"), 0, 0, + this, SLOT(slotSwitchSides()), actionCollection(), + "game_switch_sides"); + + // Some more standard game actions: Highscores, Settings. + KStdGameAction::highscores(this, SLOT(showHighScoreDialog()), actionCollection()); + KStdAction::preferences(this, SLOT(slotEditSettings()), actionCollection()); + + // Actions for the view(s). + showLastMoveAction = new KToggleAction(i18n("Show Last Move"), "lastmoves", 0, + this, SLOT(slotShowLastMove()), + actionCollection(), + "show_last_move"); + showLegalMovesAction = new KToggleAction(i18n("Show Legal Moves"), "legalmoves", 0, + this, SLOT(slotShowLegalMoves()), + actionCollection(), + "show_legal_moves"); +} + + +// ---------------------------------------------------------------- +// Methods for the engine + + +// Set the strength for the engine. We keep track of the lowest +// strength that was used during a game and if the user wins, this is +// the strength that we will store in the highscore file. + +void KReversi::setStrength(uint strength) +{ + // FIXME: 7 should be MAXSTRENGTH or something similar. + Q_ASSERT( 1 <= strength && strength <= 7 ); + + strength = QMAX(QMIN(strength, 7), 1); + m_engine->setStrength(strength); + if (m_lowestStrength < strength) + m_lowestStrength = strength; + KExtHighscore::setGameType(m_lowestStrength-1); +} + + +// ---------------------------------------------------------------- +// Slots for KActions + + +// A slot that is called when the user wants a new game. +// + +void KReversi::slotNewGame() +{ + // If already playing, ask the player if he wants to abort the old game. + if ( isPlaying() ) { + if (KMessageBox + ::warningYesNo(0, + i18n("You are already running an unfinished game. " + "If you abort the old game to start a new one, " + "the old game will be registered as a loss in " + "the highscore file.\n" + "What do you want to do?"), + i18n("Abort Current Game?"), + i18n("Abort Old Game"), + i18n("Continue Old Game")) == KMessageBox::No) + return; + + KExtHighscore::submitScore(KExtHighscore::Lost, this); + } + + m_gameOver = false; + m_cheating = false; + + m_game->newGame(); + m_competitiveGame = Prefs::competitiveGameChoice(); + m_lowestStrength = strength(); + //kdDebug() << "Competitive: " << m_competitiveGame << endl; + + // Set the state to waiting for the humans move. + setState(Ready); + + // Black always makes first move. + if (m_humanColor == White) + computerMakeMove(); +} + + +// Open an earlier saved game from the config file. +// +// FIXME: Should give a choice to load from an ordinary file (SGF?) +// + +void KReversi::slotOpenGame() +{ + KConfig *config = kapp->config(); + config->setGroup("Savegame"); + + if (loadGame(config)) + Prefs::setSkill(m_engine->strength()); + m_gameView->setHumanColor(humanColor()); +} + + +// Save a game to the config file. +// +// FIXME: Should give a choice to save as an ordinary file (SGF?) +// + +void KReversi::slotSave() +{ + KConfig *config = kapp->config(); + config->setGroup("Savegame"); + saveGame(config); + + KMessageBox::information(this, i18n("Game saved.")); +} + + +void KReversi::slotHint() +{ + Move move; + + if (state() != Ready) + return; + + setState(Thinking); + move = m_engine->computeMove(m_game, m_competitiveGame); + + setState(Hint); + m_gameView->showHint(move); + + setState(Ready); +} + + +// Takes back last set of moves +// + +void KReversi::slotUndo() +{ + if (state() != Ready) + return; + + // Can't undo anything if no moves are made. + if (m_game->moveNumber() == 0) + return; + + // Undo all moves of the same color as the last one. + Color last_color = m_game->lastMove().color(); + while (m_game->moveNumber() != 0 + && last_color == m_game->lastMove().color()) { + m_game->undoMove(); + m_gameView->removeMove(m_game->moveNumber()); + } + + // Take back one more move. + if (m_game->moveNumber() > 0) { + m_game->undoMove(); + m_gameView->removeMove(m_game->moveNumber()); + + // FIXME: Call some method in m_gameView. + m_gameView->setCurrentMove(m_game->moveNumber() - 1); + } + + if (m_game->toMove() == computerColor()) { + // Must repaint so that the new move is not shown before the old + // one is removed on the screen. + m_gameView->repaint(); + computerMakeMove(); + } + else + m_gameView->update(); +} + + +// Interrupt thinking of game engine. +// + +void KReversi::slotInterrupt() +{ + m_engine->setInterrupt(TRUE); + + // Indicate that the computer was interrupted. + showTurn(); +} + + +// Continues a move if it was interrupted earlier. +// + +void KReversi::slotContinue() +{ + if (interrupted()) + computerMakeMove(); +} + + +// Turn on or off showing of legal moves in the board view. + +void KReversi::slotShowLastMove() +{ + m_gameView->setShowLastMove(showLastMoveAction->isChecked()); +} + + +// Turn on or off showing of legal moves in the board view. + +void KReversi::slotShowLegalMoves() +{ + m_gameView->setShowLegalMoves(showLegalMovesAction->isChecked()); +} + + +void KReversi::slotSwitchSides() +{ + if (state() != Ready) + return; + + if (interrupted()) { + KMessageBox::information(this, i18n("You cannot switch sides in the middle of the computer's move."), + i18n("Notice")); + return; + } + + // It's ok to change sides before the first move. + if (m_game->moveNumber() != 0) { + int res = KMessageBox::warningContinueCancel(this, + i18n("If you switch side, your score will not be added to the highscores."), + QString::null, QString::null, "switch_side_warning"); + if ( res==KMessageBox::Cancel ) + return; + + m_cheating = true; + } + + m_humanColor = opponent(m_humanColor); + + // Update the human color in the window. + m_gameView->setHumanColor(m_humanColor); + + kapp->processEvents(); + computerMakeMove(); +} + + +// ---------------------------------------------------------------- +// Slots for the game IO + + +// Handle mouse clicks. +// + +void KReversi::slotSquareClicked(int row, int col) +{ + // Can't move when it is the computers turn. + if ( interrupted() ) { + illegalMove(); + return; + } + + if (state() == Ready) + humanMakeMove(row, col); + else if (state() == Hint) { + m_gameView->quitHint(); + setState(Ready); + } + else + illegalMove(); +} + + +// ---------------------------------------------------------------- +// Slots for the game view + + +// Show the move in the move view. +// FIXME: Move this to the gameview. + +void KReversi::handleMove(uint /*moveno*/, Move& /*move*/) +{ + showTurn(); +} + + +// A slot that is called when it is time to show whose turn it is. +// + +void KReversi::showTurn() +{ + showTurn(m_game->toMove()); +} + +void KReversi::showTurn(Color color) +{ + // If we are not playing, do nothing. + if (m_gameOver) + return; + + if (color == humanColor()) + statusBar()->message(i18n("Your turn")); + else if (color == computerColor()) { + QString message = i18n("Computer's turn"); + + // We can't use the interrupted() test here since we might be in a + // middle state when called from slotInterrupt(). + if (m_state == Thinking) + message += i18n(" (interrupted)"); + statusBar()->message(message); + } + else + statusBar()->clear(); +} + + +// A slot that is called when the game ends. +// + +void KReversi::slotGameOver() +{ + uint black = m_game->score(Black); + uint white = m_game->score(White); + + setState(Ready); + + if (black > white) + showGameOver(Black); + else if (black < white) + showGameOver(White); + else + showGameOver(Nobody); + + showTurn(Nobody); +} + + +// ---------------------------------------------------------------- +// Private methods + + +// Handle the humans move. +// + +void KReversi::humanMakeMove(int row, int col) +{ + if (state() != Ready) + return; + + Color color = m_game->toMove(); + + // Create a move from the mouse click and see if it is legal. + // If it is, then make a human move. + Move move(color, col + 1, row + 1); + if (m_game->moveIsLegal(move)) { + // Do the move. The view is automatically updated. + m_game->doMove(move); + + if (!m_game->moveIsAtAllPossible()) { + setState(Ready); + slotGameOver(); + return; + } + + if (color != m_game->toMove()) + computerMakeMove(); + } else + illegalMove(); +} + + +// Make a computer move. +// + +void KReversi::computerMakeMove() +{ + MoveList moves; + + // Check if the computer can move. + Color color = m_game->toMove(); + Color opponent = ::opponent(color); + + if (!m_game->moveIsPossible(color)) + return; + + // Make computer moves until the human can play or until the game is over. + setState(Thinking); + do { + Move move; + + if (!m_game->moveIsAtAllPossible()) { + setState(Ready); + slotGameOver(); + return; + } + + move = m_engine->computeMove(m_game, m_competitiveGame); + if (move.x() == -1) { + setState(Ready); + return; + } + usleep(300000); // Pretend we have to think hard. + + // Do the move on the board. The view is automatically updated. + //playSound("click.wav"); + m_game->doMove(move); + } while (!m_game->moveIsPossible(opponent)); + + setState(Ready); + + if (!m_game->moveIsAtAllPossible()) { + slotGameOver(); + return; + } +} + + +// Handle an attempt to make an illegal move by the human. + +void KReversi::illegalMove() +{ + KNotifyClient::event(winId(), "illegal_move", i18n("Illegal move")); +} + + +// Show things when the game is over. +// + +void KReversi::showGameOver(Color color) +{ + // If the game already was over, do nothing. + if (m_gameOver) + return; + + statusBar()->message(i18n("End of game")); + + // Get the scores. + uint human = m_game->score(humanColor()); + uint computer = m_game->score(computerColor()); + + KExtHighscore::Score score; + score.setScore(m_game->score(humanColor())); + + // Show the winner in a messagebox. + if ( color == Nobody ) { + KNotifyClient::event(winId(), "draw", i18n("Draw!")); + QString s = i18n("Game is drawn!\n\nYou : %1\nComputer: %2") + .arg(human).arg(computer); + KMessageBox::information(this, s, i18n("Game Ended")); + score.setType(KExtHighscore::Draw); + } + else if ( humanColor() == color ) { + KNotifyClient::event(winId(), "won", i18n("Game won!")); + QString s = i18n("Congratulations, you have won!\n\nYou : %1\nComputer: %2") + .arg(human).arg(computer); + KMessageBox::information(this, s, i18n("Game Ended")); + score.setType(KExtHighscore::Won); + } + else { + KNotifyClient::event(winId(), "lost", i18n("Game lost!")); + QString s = i18n("You have lost the game!\n\nYou : %1\nComputer: %2") + .arg(human).arg(computer); + KMessageBox::information(this, s, i18n("Game Ended")); + score.setType(KExtHighscore::Lost); + } + + // Store the result in the highscore file if no cheating was done, + // and only if the game was competitive. + if (!m_cheating && m_competitiveGame) { + KExtHighscore::submitScore(score, this); + } + + m_gameOver = true; +} + + +// Saves the game in the config file. +// +// Only one game at a time can be saved. +// + +void KReversi::saveGame(KConfig *config) +{ + // Stop thinking. + slotInterrupt(); + + // Write the data to the config file. + config->writeEntry("State", state()); + config->writeEntry("Strength", strength()); + config->writeEntry("Competitive", (int) m_competitiveGame); + config->writeEntry("HumanColor", (int) m_humanColor); + + // Write the moves of the game to the config object. This object + // saves itself all at once so we don't have to write the moves + // to the file ourselves. + config->writeEntry("NumberOfMoves", m_game->moveNumber()); + for (uint i = 0; i < m_game->moveNumber(); i++) { + Move move = m_game->move(i); + + QString moveString; + QString idx; + + moveString.sprintf("%d %d %d", move.x(), move.y(), (int) move.color()); + idx.sprintf("Move_%d", i + 1); + config->writeEntry(idx, moveString); + } + + // Actually write the data to file. + config->sync(); + + // Continue with the move if applicable. + slotContinue(); +} + + +// Loads the game. Only one game at a time can be saved. + +bool KReversi::loadGame(KConfig *config) +{ + slotInterrupt(); // stop thinking + + uint nmoves = config->readNumEntry("NumberOfMoves", 0); + if (nmoves==0) + return false; + + m_game->newGame(); + uint movenumber = 1; + while (nmoves--) { + // Read one move. + QString idx; + idx.sprintf("Move_%d", movenumber++); + + QStringList s = config->readListEntry(idx, ' '); + uint x = (*s.at(0)).toUInt(); + uint y = (*s.at(1)).toUInt(); + Color color = (Color)(*s.at(2)).toInt(); + + Move move(color, x, y); + m_game->doMove(move); + } + + m_humanColor = (Color) config->readNumEntry("HumanColor"); + m_competitiveGame = (bool) config->readNumEntry("Competitive"); + + m_gameView->updateBoard(TRUE); + setState(State(config->readNumEntry("State"))); + setStrength(config->readNumEntry("Strength", 1)); + + if (interrupted()) + slotContinue(); + else { + // Computer makes first move. + if (m_humanColor != m_game->toMove()) + computerMakeMove(); + } + + return true; +} + + +// ---------------------------------------------------------------- + + +void KReversi::saveProperties(KConfig *c) +{ + saveGame(c); +} + + +void KReversi::readProperties(KConfig *config) { + loadGame(config); + m_gameOver = false; + m_cheating = false; // FIXME: Is this true? It isn't saved. +} + + +void KReversi::showHighScoreDialog() +{ + KExtHighscore::show(this); +} + + +void KReversi::slotEditSettings() +{ + // If we are already editing the settings, then do nothing. + if (KConfigDialog::showDialog("settings")) + return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Prefs::self(), + KDialogBase::Swallow); + Settings *general = new Settings(0, "General"); + + dialog->addPage(general, i18n("General"), "package_settings"); + connect(dialog, SIGNAL(settingsChanged()), this, SLOT(loadSettings())); + dialog->show(); +} + + +void KReversi::configureNotifications() +{ + KNotifyDialog::configure(this); +} + + +void KReversi::loadSettings() +{ + m_humanColor = (Color) Prefs::humanColor(); + setStrength(Prefs::skill()); + + // m_competitiveGame is set at the start of a game and can only be + // downgraded during the game, never upgraded. + if ( !Prefs::competitiveGameChoice() ) + m_competitiveGame = false; + + m_gameView->loadSettings(); + + // Update the color of the human and the computer. + m_gameView->setHumanColor(humanColor()); +} + + +bool KReversi::isPlaying() const +{ + return ( m_game->moveNumber() != 0 && !m_gameOver ); +} + + +void KReversi::setState(State newState) +{ + m_state = newState; + + if (m_state == Thinking){ + kapp->setOverrideCursor(waitCursor); + stopAction->setEnabled(true); + } + else { + kapp->restoreOverrideCursor(); + stopAction->setEnabled(false); + } + + continueAction->setEnabled(interrupted()); +} + + +bool KReversi::queryExit() +{ + if ( isPlaying() ) + KExtHighscore::submitScore(KExtHighscore::Lost, this); + + return KZoomMainWindow::queryExit(); +} + + +void KReversi::writeZoomSetting(uint zoom) +{ + Prefs::setZoom(zoom); + Prefs::writeConfig(); +} + + +uint KReversi::readZoomSetting() const +{ + return Prefs::zoom(); +} + + +void KReversi::writeMenubarVisibleSetting(bool visible) +{ + Prefs::setMenubarVisible(visible); + Prefs::writeConfig(); +} + + +bool KReversi::menubarVisibleSetting() const +{ + return Prefs::menubarVisible(); +} diff --git a/kreversi/kreversi.desktop b/kreversi/kreversi.desktop new file mode 100644 index 00000000..e32a3bc4 --- /dev/null +++ b/kreversi/kreversi.desktop @@ -0,0 +1,74 @@ +[Desktop Entry] +Exec=kreversi %i %m -caption "%c" +Name=KReversi +Name[af]=Kreversi +Name[be]=Рэверсі +Name[bn]=কে-রিভার্সি +Name[ca]=Reversi +Name[eo]=Renverso +Name[hi]=के-रिवर्सी +Name[is]=Viðsnúningur +Name[ne]=केडीई रिभर्सी +Name[pa]=ਕੇ-ਰੀਵਰਸੀ +Name[pl]=Reversi +Name[sv]=Kreversi +Name[ta]=Kரிவர்ஸி +Name[tg]=KРеверси +Name[th]=หมากหนีบ - K +Name[zh_TW]=KReversi 黑白棋 +Type=Application +DocPath=kreversi/index.html +GenericName=Reversi Board Game +GenericName[be]=Настольная гульня ў рэверсі +GenericName[bg]=Игра на дъска +GenericName[bn]=ছককেন্দ্রিক খেলা রিভার্সি +GenericName[br]=Ur c'hoari taolenn Reversi +GenericName[bs]=Igra s pločom +GenericName[ca]=Joc de taula Reversi +GenericName[cs]=Desková hra Reversi +GenericName[cy]=Gêm Fwrdd Reversi +GenericName[da]=Reversi brætspil +GenericName[de]=Reversi Brettspiel +GenericName[el]=Επιτραπέζιο παιχνίδι Reversi +GenericName[eo]="Renverso"-Tabuloludo +GenericName[es]=Juego de tablero Reversi +GenericName[et]=Lauamäng Reversi +GenericName[eu]=Reversi mahai-jokoa +GenericName[fa]=بازی Reversi Board +GenericName[fi]=Othello +GenericName[fr]=Jeu de plateau Reversi +GenericName[he]=רברסי, משחק לוח +GenericName[hr]=Reversi igra na ploči +GenericName[hu]=Reversi +GenericName[is]=Reversi borðleikur +GenericName[it]=Reversi, gioco da tavolo +GenericName[ja]=リバーシゲーム +GenericName[km]=ល្បែងក្ដារ Reversi +GenericName[ko]=리버시 보드 게임 +GenericName[lt]=Reversi stalo žaidimas +GenericName[lv]=Reversā galda spēle +GenericName[mk]=Игра на табла Reversi +GenericName[nb]=Brettspillet reversi +GenericName[nds]=Reversi-Brettspeel +GenericName[ne]=रिभर्सी बोर्ड खेल +GenericName[nl]=Reversi-bordspel +GenericName[nn]=Brettspelet reversi +GenericName[pl]=Gra planszowa Reversi +GenericName[pt]=Jogo de Tabuleiro Reversi +GenericName[pt_BR]=Jogo de tabuleiro como Reversi +GenericName[ru]=Реверси +GenericName[se]=Duolbbášspeallu reversi +GenericName[sk]=Stolová hra Reversi +GenericName[sl]=Namizna igra Reversi +GenericName[sr]=Игра на табли Reversi +GenericName[sr@Latn]=Igra na tabli Reversi +GenericName[sv]=Othello brädspel +GenericName[ta]=ரிவர்சி பலகை விளையாட்டு +GenericName[uk]=Гра на дошці (реверсі) +GenericName[wa]=Djeu d' platea Reversi +GenericName[zh_TW]=黑白棋棋盤遊戲 +Terminal=false +Icon=kreversi +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;BoardGame; diff --git a/kreversi/kreversi.h b/kreversi/kreversi.h new file mode 100644 index 00000000..17599fa2 --- /dev/null +++ b/kreversi/kreversi.h @@ -0,0 +1,178 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni <mweilguni@sime.com> + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#ifndef KREVERSI_H +#define KREVERSI_H + + +#include "kzoommainwindow.h" + +#include "Score.h" +#include "Game.h" +#include "Engine.h" +//#include "board.h" +#include "qreversigame.h" +#include "qreversigameview.h" + + +class QLabel; + +class KAction; + + +class KReversi : public KZoomMainWindow +{ + Q_OBJECT + +public: + + enum State { Ready, Thinking, Hint}; + + KReversi(); + ~KReversi(); + + bool isPlaying() const; + + // Methods that deal with the game + Color toMove() const { return m_game->toMove(); } + Color humanColor() const { return m_humanColor; } + Color computerColor() const { return opponent(m_humanColor); } + + // Methods that deal with the engine. + void setStrength(uint); + uint strength() const { return m_engine->strength(); } + void interrupt() { m_engine->setInterrupt(TRUE); } + bool interrupted() const { return (m_game->toMove() == computerColor() + && m_state == Ready); } + + // State of the program (Hint, Ready, Thinking, etc). + void setState(State); + State state() const { return m_state; } + +private: + // Initialisation + void createKActions(); + + // View functions. + QString getPlayerName(); + + virtual void writeZoomSetting(uint zoom); + virtual uint readZoomSetting() const; + virtual void writeMenubarVisibleSetting(bool visible); + virtual bool menubarVisibleSetting() const; + + virtual void saveProperties(KConfig *); + virtual void readProperties(KConfig *); + virtual bool queryExit(); + + +private slots: + + // Slots for KActions. + void slotNewGame(); + void slotOpenGame(); + void slotSave(); + void slotHint(); + void slotUndo(); + void slotSwitchSides(); + + // Interrupt and continue the engines thinking (also KActions). + void slotInterrupt(); + void slotContinue(); + void slotShowLastMove(); + void slotShowLegalMoves(); + + // Slots for game IO + void slotSquareClicked(int, int); + + // Misc slots. + void configureNotifications(); + + // Some dialogs and other misc stuff. + void showHighScoreDialog(); + void slotEditSettings(); + void loadSettings(); + + public slots: + // Slots for the view. + void handleMove(uint moveno, Move &move); + void showTurn(); + void showTurn(Color color); + void slotGameOver(); + +private: + + // Private methods + void humanMakeMove(int row, int col); + void computerMakeMove(); + void illegalMove(); + void showGameOver(Color); + + void saveGame(KConfig *); + bool loadGame(KConfig *); + + +private: + // Some Actions that need to be manipulated. + KAction *stopAction; + KAction *continueAction; + + KToggleAction *showLastMoveAction; + KToggleAction *showLegalMovesAction; + + // The game itself and game properties + QReversiGame *m_game; // The main document - the game + + Color m_humanColor; // The Color of the human player. + bool m_gameOver; // True if the game is over + bool m_cheating; // True if the user has changed sides + uint m_lowestStrength; // Lowest strength during the game. + bool m_competitiveGame;// True if the game has been + // competitive during all moves so far. + + State m_state; // Ready, Thinking, Hint + Engine *m_engine; // The AI that creates the computers moves. + + // Widgets + QReversiGameView *m_gameView; // The board widget. +}; + + +#endif + diff --git a/kreversi/kreversi.kcfg b/kreversi/kreversi.kcfg new file mode 100644 index 00000000..c1dc6e52 --- /dev/null +++ b/kreversi/kreversi.kcfg @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <include>kstandarddirs.h</include> + <include>kglobal.h</include> + <kcfgfile name="kreversirc"/> + <group name="Game"> + <entry name="Grayscale" type="Bool"> + <label>Whether to use a grayscale board instead of colored.</label> + <default>false</default> + </entry> + <entry name="HumanColor" type="Int"> + <label>The human color.</label> + <default>1</default> + </entry> + <entry name="ComputerColor" type="Int"> + <label>The computer color.</label> + <default>0</default> + </entry> + <entry name="Animation" type="Bool"> + <label>Whether to use animations.</label> + <default>true</default> + </entry> + <entry name="AnimationSpeed" type="Int"> + <label>The speed of the animations.</label> + <default>4</default> + <min>1</min> + <max>10</max> + </entry> + <entry name="Zoom" type="Int"> + <label>The zoom factor of the board.</label> + <default>100</default> + <min>10</min> + <max>300</max> + </entry> + <entry name="CompetitiveGameChoice" type="Bool"> + <label>Whether to play competitively in contrast to casually.</label> + <default>true</default> + </entry> + <entry name="skill" type="Int"> + <label>The strength of the computer player.</label> + <default>1</default> + <min>1</min> + <max>7</max> + </entry> + <entry name="BackgroundImageChoice" type="Bool"> + <label>Whether to use a background image.</label> + <default>true</default> + </entry> + <entry name="BackgroundColor" type="Color"> + <label>The background color to use.</label> + <default>#ffffff</default> + </entry> + <entry name="BackgroundImage" type="Path"> + <label>Image to use as background.</label> + <code>#define PICDATA(x) KGlobal::dirs()->findResource("appdata", QString("pics/")+ x)</code> + <default code="true">PICDATA("background/Light_Wood.png")</default> + </entry> + <entry name="MenubarVisible" type="Bool" key="menubar visible"> + <label>Whether the menubar is visible.</label> + <default>true</default> + </entry> + </group> +</kcfg> diff --git a/kreversi/kreversiui.rc b/kreversi/kreversiui.rc new file mode 100644 index 00000000..304b87b2 --- /dev/null +++ b/kreversi/kreversiui.rc @@ -0,0 +1,45 @@ +<!DOCTYPE kpartgui> +<kpartgui name="kreversi" version="2"> + +<ActionProperties> + <Action name="game_stop" icon="stop"/> +</ActionProperties> + +<MenuBar> + <Menu name="game"><text>&Game</text> + <Action name="game_sound"/> + </Menu> + <Menu name="move"><text>&Move</text> + <Action name="game_undo"/> + <Action name="game_hint"/> + <Action name="game_switch_sides"/> + <Separator/> + <Action name="game_stop"/> + <Action name="game_continue"/> + </Menu> + +</MenuBar> + +<ToolBar name="mainToolBar"><text>Main Toolbar</text> + <Action name="game_new"/> + <Action name="game_stop"/> + <Action name="game_continue"/> + <Action name="game_undo"/> + <Action name="options_show_menubar"/> +</ToolBar> +<ToolBar name="viewToolBar"><text>View Toolbar</text> + <Action name="game_hint"/> + <Action name="show_last_move"/> + <Action name="show_legal_moves"/> +</ToolBar> + +<Menu name="popup"> + <Action name="options_show_menubar"/> + <Separator/> + <Action name="game_new"/> + <Action name="game_highscores"/> + <Separator/> + <Action name="game_quit"/> +</Menu> + +</kpartgui> diff --git a/kreversi/kzoommainwindow.cpp b/kreversi/kzoommainwindow.cpp new file mode 100644 index 00000000..4da50935 --- /dev/null +++ b/kreversi/kzoommainwindow.cpp @@ -0,0 +1,135 @@ +/* + This file is part of the KDE games library + Copyright (C) 2004 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#include "kzoommainwindow.h" +#include "kzoommainwindow.moc" + +#include <kaction.h> +#include <kstdaction.h> +#include <kmenubar.h> +#include <kcmenumngr.h> + + +KZoomMainWindow::KZoomMainWindow(uint min, uint max, uint step, + const char *name) + : KMainWindow(0, name), m_zoomStep(step), m_minZoom(min), m_maxZoom(max) +{ + installEventFilter(this); + + m_zoomInAction = + KStdAction::zoomIn(this, SLOT(zoomIn()), actionCollection()); + m_zoomOutAction = + KStdAction::zoomOut(this, SLOT(zoomOut()), actionCollection()); + m_menu = + KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection()); +} + + +void KZoomMainWindow::init(const char *popupName) +{ + // zoom + setZoom(readZoomSetting()); + + // menubar + m_menu->setChecked( menubarVisibleSetting() ); + toggleMenubar(); + + // context popup + if (popupName) { + QPopupMenu *popup = + static_cast<QPopupMenu *>(factory()->container(popupName, this)); + Q_ASSERT(popup); + if (popup) + KContextMenuManager::insert(this, popup); + } +} + +void KZoomMainWindow::addWidget(QWidget *widget) +{ + widget->adjustSize(); + + QWidget *tlw = widget->topLevelWidget(); + KZoomMainWindow *zm = + static_cast<KZoomMainWindow *>(tlw->qt_cast("KZoomMainWindow")); + + Q_ASSERT(zm); + zm->m_widgets.append(widget); + connect(widget, SIGNAL(destroyed()), zm, SLOT(widgetDestroyed())); +} + + +void KZoomMainWindow::widgetDestroyed() +{ + m_widgets.remove(static_cast<const QWidget *>(sender())); +} + + +bool KZoomMainWindow::eventFilter(QObject *o, QEvent *e) +{ + if ( e->type()==QEvent::LayoutHint ) + setFixedSize(minimumSize()); // because K/QMainWindow + // does not manage fixed central widget + // with hidden menubar... + return KMainWindow::eventFilter(o, e); +} + + +void KZoomMainWindow::setZoom(uint zoom) +{ + m_zoom = zoom; + writeZoomSetting(m_zoom); + + QPtrListIterator<QWidget> it(m_widgets); + for (; it.current(); ++it) + (*it)->adjustSize(); + + m_zoomOutAction->setEnabled( m_zoom > m_minZoom ); + m_zoomInAction->setEnabled( m_zoom < m_maxZoom ); +} + + +void KZoomMainWindow::zoomIn() +{ + setZoom(m_zoom + m_zoomStep); +} + + +void KZoomMainWindow::zoomOut() +{ + Q_ASSERT( m_zoom >= m_zoomStep ); + setZoom(m_zoom - m_zoomStep); +} + + +void KZoomMainWindow::toggleMenubar() +{ + if ( m_menu->isChecked() ) + menuBar()->show(); + else + menuBar()->hide(); +} + + +bool KZoomMainWindow::queryExit() +{ + writeMenubarVisibleSetting(m_menu->isChecked()); + + return KMainWindow::queryExit(); +} diff --git a/kreversi/kzoommainwindow.h b/kreversi/kzoommainwindow.h new file mode 100644 index 00000000..dee04139 --- /dev/null +++ b/kreversi/kzoommainwindow.h @@ -0,0 +1,138 @@ +/* + This file is part of the KDE games library + Copyright (C) 2004 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#ifndef KZOOMMAINWINDOW_H +#define KZOOMMAINWINDOW_H + + +#include <kmainwindow.h> + + +class KToggleAction; + + +/** + * KZoomMainWindow is a main window of fixed size. Its size can be + * modified with the "zoom in"/"zoom out" actions. + * + * It manages one or several widgets: their adjustSize() method is + * called whenever the zoom level is changed. + * The usual implementation for those widget is to redefine adjustSize() + * with code like: + * /code + * setFixedSize(newsize); + * /endcode + * + * This class also has a "show/hide menubar" action and allows the use + * of a context popup menu (useful to restore the menubar when hidden). + */ + +class KZoomMainWindow : public KMainWindow +{ + Q_OBJECT +public: + /** Constructor. */ + KZoomMainWindow(uint minZoom, uint maxZoom, uint zoomStep, + const char *name = 0); + + /** Add a widget to be managed i.e. the adjustSize() method of the + * widget is called whenever the zoom is changed. + * This function assumes that the topLevelWidget() is the KZoomMainWindow. + */ + static void addWidget(QWidget *widget); + + uint zoom() const { return m_zoom; } + +public slots: + void zoomIn(); + void zoomOut(); + void toggleMenubar(); + +protected: + /** You need to call this after the createGUI or setupGUI method + * is called. + * @param popupName is the name of the context popup menu as defined in + * the ui.rc file. + */ + void init(const char *popupName = 0); + + virtual void setZoom(uint zoom); + virtual bool eventFilter(QObject *o, QEvent *e); + virtual bool queryExit(); + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::setZoom(zoom); + * Settings::writeConfig(); + * /endcode + */ + virtual void writeZoomSetting(uint zoom) = 0; + + /** Youneed to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * return Settings::zoom(); + * /endcode + */ + virtual uint readZoomSetting() const = 0; + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::setMenubarVisible(visible); + * Settings::writeConfig(); + * /endcode + */ + virtual void writeMenubarVisibleSetting(bool visible) = 0; + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::menubarVisible(); + * /endcode + */ + virtual bool menubarVisibleSetting() const = 0; + +private slots: + void widgetDestroyed(); + +private: + uint m_zoom; + uint m_zoomStep; + uint m_minZoom; + uint m_maxZoom; + + QPtrList<QWidget> m_widgets; + + KAction *m_zoomInAction; + KAction *m_zoomOutAction; + KToggleAction *m_menu; + + class KZoomMainWindowPrivate; + KZoomMainWindowPrivate *d; +}; + + +#endif diff --git a/kreversi/main.cpp b/kreversi/main.cpp new file mode 100644 index 00000000..345e97da --- /dev/null +++ b/kreversi/main.cpp @@ -0,0 +1,87 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni <mweilguni@sime.com> + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include <kapplication.h> +#include <kimageio.h> +#include <kcmdlineargs.h> +#include <kaboutdata.h> +#include <khighscore.h> + +#include "version.h" +#include "kreversi.h" +#include "highscores.h" + + +static const char description[] = I18N_NOOP("KDE Board Game"); + +int main(int argc, char **argv) +{ + KHighscore::init("kreversi"); + + KAboutData aboutData( "kreversi", I18N_NOOP("KReversi"), + KREVERSI_VERSION, description, KAboutData::License_GPL, + "(c) 1997-2000, Mario Weilguni"); + aboutData.addAuthor("Mario Weilguni",0, "mweilguni@sime.com"); + aboutData.addAuthor("Benjamin Meyer",0, "ben+kreversi@meyerhome.net"); + aboutData.addCredit("Mats Luthman", I18N_NOOP("Game engine, ported from his JAVA applet."), 0); + aboutData.addCredit("Stephan Kulow", I18N_NOOP("Comments and bugfixes."), 0); + aboutData.addCredit("Arne Klaassen", I18N_NOOP("Raytraced chips."), 0); + aboutData.addCredit("Inge Wallin", I18N_NOOP("Cleaning, bugfixes, some enhancements."), 0); + + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + // used for loading background pixmaps + KImageIO::registerFormats(); + KExtHighscore::ExtManager highscores; + + if (a.isRestored()){ + RESTORE(KReversi) + } + else { + KReversi *kreversi = new KReversi; + a.setMainWidget(kreversi); + kreversi->show(); + } + + return a.exec(); +} + diff --git a/kreversi/pics/Makefile.am b/kreversi/pics/Makefile.am new file mode 100644 index 00000000..e962996b --- /dev/null +++ b/kreversi/pics/Makefile.am @@ -0,0 +1,8 @@ +pics_DATA = chips.png chips_mono.png + +picsdir = $(kde_datadir)/kreversi/pics + +SUBDIRS = background + +EXTRA_DIST = $(pics_DATA) + diff --git a/kreversi/pics/background/Dark_Wood.png b/kreversi/pics/background/Dark_Wood.png Binary files differnew file mode 100644 index 00000000..cf487169 --- /dev/null +++ b/kreversi/pics/background/Dark_Wood.png diff --git a/kreversi/pics/background/Earth.png b/kreversi/pics/background/Earth.png Binary files differnew file mode 100644 index 00000000..fd9d4f82 --- /dev/null +++ b/kreversi/pics/background/Earth.png diff --git a/kreversi/pics/background/Granite.png b/kreversi/pics/background/Granite.png Binary files differnew file mode 100644 index 00000000..6fd04e93 --- /dev/null +++ b/kreversi/pics/background/Granite.png diff --git a/kreversi/pics/background/Hexagon.png b/kreversi/pics/background/Hexagon.png Binary files differnew file mode 100644 index 00000000..4a7b3de1 --- /dev/null +++ b/kreversi/pics/background/Hexagon.png diff --git a/kreversi/pics/background/Light_Wood.png b/kreversi/pics/background/Light_Wood.png Binary files differnew file mode 100644 index 00000000..04877a47 --- /dev/null +++ b/kreversi/pics/background/Light_Wood.png diff --git a/kreversi/pics/background/Makefile.am b/kreversi/pics/background/Makefile.am new file mode 100644 index 00000000..e106a1f7 --- /dev/null +++ b/kreversi/pics/background/Makefile.am @@ -0,0 +1,8 @@ + +bg_DATA = Dark_Wood.png Granite.png Light_Wood.png Ocean.png Puzzle.png \ +Earth.png Hexagon.png Mystique.png Pipes.png Stones.png + +bgdir = $(kde_datadir)/kreversi/pics/background + +EXTRA_DIST = $(bg_DATA) + diff --git a/kreversi/pics/background/Mystique.png b/kreversi/pics/background/Mystique.png Binary files differnew file mode 100644 index 00000000..55b9b960 --- /dev/null +++ b/kreversi/pics/background/Mystique.png diff --git a/kreversi/pics/background/Ocean.png b/kreversi/pics/background/Ocean.png Binary files differnew file mode 100644 index 00000000..6e420d6e --- /dev/null +++ b/kreversi/pics/background/Ocean.png diff --git a/kreversi/pics/background/Pipes.png b/kreversi/pics/background/Pipes.png Binary files differnew file mode 100644 index 00000000..111edef0 --- /dev/null +++ b/kreversi/pics/background/Pipes.png diff --git a/kreversi/pics/background/Puzzle.png b/kreversi/pics/background/Puzzle.png Binary files differnew file mode 100644 index 00000000..4033fd80 --- /dev/null +++ b/kreversi/pics/background/Puzzle.png diff --git a/kreversi/pics/background/Stones.png b/kreversi/pics/background/Stones.png Binary files differnew file mode 100644 index 00000000..4ba7dc7f --- /dev/null +++ b/kreversi/pics/background/Stones.png diff --git a/kreversi/pics/chips.png b/kreversi/pics/chips.png Binary files differnew file mode 100644 index 00000000..5a6ffda6 --- /dev/null +++ b/kreversi/pics/chips.png diff --git a/kreversi/pics/chips_mono.png b/kreversi/pics/chips_mono.png Binary files differnew file mode 100644 index 00000000..049d8c66 --- /dev/null +++ b/kreversi/pics/chips_mono.png diff --git a/kreversi/prefs.kcfgc b/kreversi/prefs.kcfgc new file mode 100644 index 00000000..173c0e55 --- /dev/null +++ b/kreversi/prefs.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=kreversi.kcfg +#IncludeFiles=defines.h +ClassName=Prefs +Singleton=true +CustomAdditions=false +Mutators=skill,Zoom,MenubarVisible diff --git a/kreversi/qreversigame.cpp b/kreversi/qreversigame.cpp new file mode 100644 index 00000000..d31bac4a --- /dev/null +++ b/kreversi/qreversigame.cpp @@ -0,0 +1,93 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni <mweilguni@sime.com> + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include "qreversigame.h" + + +// ================================================================ +// class QReversiGame + + +QReversiGame::QReversiGame(QObject *parent) + : QObject(parent), Game() +{ +} + + +QReversiGame::~QReversiGame() +{ +} + + +void QReversiGame::newGame() +{ + Game::newGame(); + + emit sig_newGame(); +} + + +bool QReversiGame::doMove(Move move) +{ + bool retval = Game::doMove(move); + if (!retval) + return false; + + emit sig_move(m_moveNumber, move); + + if (!Game::moveIsAtAllPossible()) + emit sig_gameOver(); + + return retval; +} + + +bool QReversiGame::undoMove() +{ + bool retval = Game::undoMove(); + + // Update all views. + emit sig_update(); + + return retval; +} + + +#include "qreversigame.moc" + diff --git a/kreversi/qreversigame.h b/kreversi/qreversigame.h new file mode 100644 index 00000000..d1712832 --- /dev/null +++ b/kreversi/qreversigame.h @@ -0,0 +1,101 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 2005 by Inge Wallin <inge@lysator.liu.se> + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#ifndef __QREVERSIGAME__H__ +#define __QREVERSIGAME__H__ + +#include <qwidget.h> +#include <qpixmap.h> + +#include "Position.h" +#include "Game.h" +#include "Move.h" + + +class KConfig; + + + +// The main document class in the reversi program. The thing that +// makes this a QReversiGame instead of just a ReversiGame is that it +// emits signals that can be used to update a view. +// +// Signals: +// updateBoard() +// score() +// turn(Color) +// gameOver() +// + +class QReversiGame : public QObject, public Game { + Q_OBJECT + + public: + QReversiGame(QObject *parent = 0); + ~QReversiGame(); + + // Methods dealing with the game + void newGame(); + bool doMove(Move move); + bool undoMove(); +#if 0 + void loadSettings(); + + bool loadGame(KConfig *, bool noupdate = FALSE); + void saveGame(KConfig *); +#endif + + signals: + void sig_newGame(); + void sig_move(uint, Move&); // A move has just been done. + void sig_update(); // Some other change than a move has been done + // Example: loadFile(), undoMove(); + void sig_gameOver(); // The game is over. + + // FIXME: To be removed: + //void updateBoard(); + //void sig_score(); + //void turn(Color); + +private: + // No members. +}; + + +#endif + diff --git a/kreversi/qreversigameview.cpp b/kreversi/qreversigameview.cpp new file mode 100644 index 00000000..92812657 --- /dev/null +++ b/kreversi/qreversigameview.cpp @@ -0,0 +1,299 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 2005 by Inge Wallin <inge@lysator.liu.se> + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include <qlayout.h> +#include <qwidget.h> +#include <qlabel.h> + +#include <klocale.h> +#include <kdialog.h> + +#if 0 +#include <unistd.h> + +#include <qpainter.h> +#include <qfont.h> + +#include <kapplication.h> +#include <kstandarddirs.h> +#include <kconfig.h> +#include <knotifyclient.h> +#include <klocale.h> +#include <kexthighscore.h> +#include <kdebug.h> + +#include "board.h" +#include "Engine.h" +#endif +#include "prefs.h" + +#include "qreversigame.h" +#include "qreversigameview.h" + + +// ================================================================ +// class StatusWidget + + +StatusWidget::StatusWidget(const QString &text, QWidget *parent) + : QWidget(parent, "status_widget") +{ + QHBoxLayout *hbox = new QHBoxLayout(this, 0, KDialog::spacingHint()); + QLabel *label; + + m_textLabel = new QLabel(text, this); + hbox->addWidget(m_textLabel); + + m_pixLabel = new QLabel(this); + hbox->addWidget(m_pixLabel); + + label = new QLabel(":", this); + hbox->addWidget(label); + + m_scoreLabel = new QLabel(this); + hbox->addWidget(m_scoreLabel); +} + + +// Set the text label +// + +void StatusWidget::setText(const QString &string) +{ + m_textLabel->setText(string); +} + + +// Set the pixel label - used to show the color. +// + +void StatusWidget::setPixmap(const QPixmap &pixmap) +{ + m_pixLabel->setPixmap(pixmap); +} + + +// Set the score label - used to write the number of pieces. +// + +void StatusWidget::setScore(uint s) +{ + m_scoreLabel->setText(QString::number(s)); +} + + +// ================================================================ +// class QReversiGameView + + +QReversiGameView::QReversiGameView(QWidget *parent, QReversiGame *game) + : QWidget(parent, "gameview") +{ + // Store a pointer to the game. + m_game = game; + + // The widget stuff + createView(); + + // Other initializations. + m_humanColor = Nobody; + + // Connect the game to the view. + connect(m_game, SIGNAL(sig_newGame()), this, SLOT(newGame())); + connect(m_game, SIGNAL(sig_move(uint, Move&)), + this, SLOT(moveMade(uint, Move&))); + connect(m_game, SIGNAL(sig_update()), this, SLOT(updateView())); + // The sig_gameOver signal is not used by the view. + + // Reemit the signal from the board. + connect(m_boardView, SIGNAL(signalSquareClicked(int, int)), + this, SLOT(squareClicked(int, int))); +} + + +QReversiGameView::~QReversiGameView() +{ +} + + +// Create the entire view. Only called once from the constructor. + +void QReversiGameView::createView() +{ + QGridLayout *layout = new QGridLayout(this, 4, 2); + + // The board + m_boardView = new QReversiBoardView(this, m_game); + m_boardView->loadSettings(); // Load the pixmaps used in the status widgets. + layout->addMultiCellWidget(m_boardView, 0, 3, 0, 0); + + // The status widgets + m_blackStatus = new StatusWidget(QString::null, this); + m_blackStatus->setPixmap(m_boardView->chipPixmap(Black, 20)); + layout->addWidget(m_blackStatus, 0, 1); + m_whiteStatus = new StatusWidget(QString::null, this); + m_whiteStatus->setPixmap(m_boardView->chipPixmap(White, 20)); + layout->addWidget(m_whiteStatus, 1, 1); + + // The "Moves" label + QLabel *movesLabel = new QLabel( i18n("Moves"), this); + movesLabel->setAlignment(AlignCenter); + layout->addWidget(movesLabel, 2, 1); + + // The list of moves. + m_movesView = new QListBox(this, "moves"); + m_movesView->setMinimumWidth(150); + layout->addWidget(m_movesView, 3, 1); +} + + +// ---------------------------------------------------------------- +// Slots + + +// Recieves the sig_newGame signal from the game. + +void QReversiGameView::newGame() +{ + m_boardView->updateBoard(true); + m_movesView->clear(); + updateStatus(); +} + + +// Recieves the sig_move signal from the game. + +void QReversiGameView::moveMade(uint moveNum, Move &move) +{ + //FIXME: Error checks. + QString colorsWB[] = { + i18n("White"), + i18n("Black") + }; + QString colorsRB[] = { + i18n("Red"), + i18n("Blue") + }; + + // Insert the new move in the listbox and mark it as the current one. + m_movesView->insertItem(QString("%1. %2 %3") + .arg(moveNum) + .arg(Prefs::grayscale() ? colorsWB[move.color()] + : colorsRB[move.color()]) + .arg(move.asString())); + m_movesView->setCurrentItem(moveNum - 1); + m_movesView->ensureCurrentVisible(); + + // Animate all changed pieces. + m_boardView->animateChanged(move); + m_boardView->updateBoard(); + + // Update the score. + updateStatus(); +} + + +// Recieves the sig_update signal from the game, and can be called +// whenever a total update of the view is required. + +void QReversiGameView::updateView() +{ + m_boardView->updateBoard(true); + updateMovelist(); + updateStatus(); +} + + +// Only updates the status widgets (score). + +void QReversiGameView::updateStatus() +{ + m_blackStatus->setScore(m_game->score(Black)); + m_whiteStatus->setScore(m_game->score(White)); +} + + +// Only updates the status board. + +void QReversiGameView::updateBoard(bool force) +{ + m_boardView->updateBoard(force); +} + + +// Only updates the movelist. This method regenerates the list from +// scratch. + +void QReversiGameView::updateMovelist() +{ + // FIXME: NYI +} + + +// This special slot is just because the external program doesn't have +// access to the internal board view. +// + +void QReversiGameView::squareClicked(int row, int col) +{ + emit signalSquareClicked(row, col); +} + + +// ---------------------------------------------------------------- +// Other public methods. + + +void QReversiGameView::setHumanColor(Color color) +{ + m_humanColor = color; + + if (color == Black) { + m_blackStatus->setText(i18n("You")); + m_whiteStatus->setText(""); + } + else { + m_blackStatus->setText(""); + m_whiteStatus->setText(i18n("You")); + } +} + + +#include "qreversigameview.moc" + diff --git a/kreversi/qreversigameview.h b/kreversi/qreversigameview.h new file mode 100644 index 00000000..a3059a25 --- /dev/null +++ b/kreversi/qreversigameview.h @@ -0,0 +1,160 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 2005 by Inge Wallin <inge@lysator.liu.se> + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI 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, or (at your option) + * any later version. + * + * KREVERSI 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 KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#ifndef __QREVERSIGAMEVIEW__H__ +#define __QREVERSIGAMEVIEW__H__ + + +#include <qlistbox.h> + +#include "Score.h" +#include "Move.h" +#include "board.h" + + +class KConfig; + + +class QLabel; + +class QReversiGame; + + +class StatusWidget : public QWidget +{ + Q_OBJECT + +public: + StatusWidget(const QString &text, QWidget *parent); + + void setText(const QString &string); + void setPixmap(const QPixmap &pixmap); + void setScore(uint score); + +private: + QLabel *m_textLabel; + QLabel *m_pixLabel; + QLabel *m_scoreLabel; +}; + + +// The main game view + +class QReversiGameView : public QWidget { + Q_OBJECT + +public: + + QReversiGameView(QWidget *parent, QReversiGame *game); + ~QReversiGameView(); + + // Proxy methods for the board view + void showHint(Move move) { m_boardView->showHint(move); } + void quitHint() { m_boardView->quitHint(); } + + void setShowLegalMoves(bool show){ m_boardView->setShowLegalMoves(show); } + void setShowMarks(bool show) { m_boardView->setShowMarks(show); } + void setShowLastMove(bool show){ m_boardView->setShowLastMove(show); } + + void setAnimationSpeed(uint speed){m_boardView->setAnimationSpeed(speed);} + + // To get the pixmap for the status view + QPixmap chipPixmap(Color color, uint size) const + { return m_boardView->chipPixmap(color, size); } + + // Proxy methods for the movelist + // FIXME: Not all of these need to be externally reachable + void insertMove(QString moveString) { m_movesView->insertItem(moveString); } + void removeMove(int moveNum) { + m_movesView->removeItem(moveNum); + updateStatus(); + } + void setCurrentMove(int moveNum) { + m_movesView->setCurrentItem(moveNum); + m_movesView->ensureCurrentVisible(); + } + + // The status widgets. + void setHumanColor(Color color); + + // Starts all: emits some signal, so it can't be called from + // constructor + void start() { m_boardView->start(); } + + // Used by the outer KZoomMainWindow class. + void adjustSize() { m_boardView->adjustSize(); } + + void loadSettings() { m_boardView->loadSettings(); } + + +public slots: + void newGame(); + void moveMade(uint moveNum, Move &move); + + void updateView(); // Update the entire view. + void updateStatus(); // Update the status widgets (score) + void updateBoard(bool force = FALSE); // Update the board. + void updateMovelist(); // Update the move list. + +signals: + void signalSquareClicked(int, int); + +private slots: + // Internal slot to reemit the boards signal. + void squareClicked(int, int); + +private: + void createView(); + +private: + + // Pointer to the game we are displaying + QReversiGame *m_game; // Pointer to the game object (not owner). + + Color m_humanColor; + + // Widgets in the view. + QReversiBoardView *m_boardView; + QListBox *m_movesView; + StatusWidget *m_blackStatus; + StatusWidget *m_whiteStatus; +}; + + +#endif + diff --git a/kreversi/settings.ui b/kreversi/settings.ui new file mode 100644 index 00000000..b7c59ff5 --- /dev/null +++ b/kreversi/settings.ui @@ -0,0 +1,329 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>Settings</class> +<widget class="QWidget"> + <property name="name"> + <cstring>Settings</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>431</width> + <height>323</height> + </rect> + </property> + <property name="caption"> + <string>Settings</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QFrame"> + <property name="name"> + <cstring>frame3</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer row="4" column="0"> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>kcfg_Grayscale</cstring> + </property> + <property name="text"> + <string>&Grayscale chips</string> + </property> + </widget> + <widget class="QButtonGroup" row="0" column="1" rowspan="2" colspan="1"> + <property name="name"> + <cstring>kcfg_CompetitiveGameChoice</cstring> + </property> + <property name="title"> + <string>Play Game</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>CasualGameChoice</cstring> + </property> + <property name="text"> + <string>Casually</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>CompetitiveGameChoice</cstring> + </property> + <property name="text"> + <string>Competitively</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox" row="2" column="1"> + <property name="name"> + <cstring>skillGroup</cstring> + </property> + <property name="title"> + <string>&Computer Skill</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QSlider" row="0" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>kcfg_skill</cstring> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="maxValue"> + <number>7</number> + </property> + <property name="pageStep"> + <number>1</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>NoMarks</enum> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>Beginner</cstring> + </property> + <property name="text"> + <string>Beginner</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignLeft</set> + </property> + </widget> + <widget class="QLabel" row="1" column="2"> + <property name="name"> + <cstring>Expert</cstring> + </property> + <property name="text"> + <string>Expert</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>Average</cstring> + </property> + <property name="text"> + <string>Average</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox" row="2" column="0"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Animation Speed</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Slow</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignLeft</set> + </property> + </widget> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Fast</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + <widget class="QSlider" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_AnimationSpeed</cstring> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="maxValue"> + <number>10</number> + </property> + <property name="lineStep"> + <number>1</number> + </property> + <property name="pageStep"> + <number>1</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>NoMarks</enum> + </property> + </widget> + </grid> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>kcfg_Animation</cstring> + </property> + <property name="text"> + <string>&Animation</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QButtonGroup" row="3" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_BackgroundImageChoice</cstring> + </property> + <property name="title"> + <string>&Background</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton" row="0" column="0"> + <property name="name"> + <cstring>BackgroundColorChoice</cstring> + </property> + <property name="text"> + <string>Color:</string> + </property> + </widget> + <widget class="KURLRequester" row="1" column="1"> + <property name="name"> + <cstring>kcfg_BackgroundImage</cstring> + </property> + </widget> + <widget class="QRadioButton" row="1" column="0"> + <property name="name"> + <cstring>BackgroundImageChoice</cstring> + </property> + <property name="text"> + <string>&Image:</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="KColorButton" row="0" column="1"> + <property name="name"> + <cstring>kcfg_BackgroundColor</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + <property name="color"> + <color> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </property> + </widget> + </grid> + </widget> + </grid> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>kcfg_Animation</sender> + <signal>toggled(bool)</signal> + <receiver>groupBox2</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>BackgroundColorChoice</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_BackgroundColor</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>BackgroundImageChoice</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_BackgroundImage</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> +</includehints> +</UI> diff --git a/kreversi/sounds/Makefile.am b/kreversi/sounds/Makefile.am new file mode 100644 index 00000000..83348417 --- /dev/null +++ b/kreversi/sounds/Makefile.am @@ -0,0 +1,7 @@ +soundsdir = $(kde_datadir)/kreversi/sounds +sounds_DATA = reversi-click.wav reversi-won.wav + +EXTRA_DIST = $(sounds_DATA) + +appdatadir = $(kde_datadir)/kreversi +appdata_DATA = eventsrc
\ No newline at end of file diff --git a/kreversi/sounds/eventsrc b/kreversi/sounds/eventsrc new file mode 100644 index 00000000..cc525204 --- /dev/null +++ b/kreversi/sounds/eventsrc @@ -0,0 +1,566 @@ +[!Global!] +IconName=kreversi +Comment=KReversi +Comment[be]=Рэверсі +Comment[bn]=কে-রিভার্সি +Comment[ca]=Reversi +Comment[is]=Viðsnúningur +Comment[ne]=केडीई रिभर्सी +Comment[sv]=Othello +Comment[zh_TW]=KReversi 黑白棋 + +[click] +Name=Click +Name[be]=Пстрычка +Name[bg]=Щракване +Name[bn]=ক্লিক +Name[br]=Klik +Name[bs]=Klikni +Name[ca]=Clic +Name[cs]=Kliknutí +Name[cy]=Clicio +Name[da]=Klik +Name[de]=Klick +Name[el]=Κλικ +Name[eo]=Kliki +Name[es]=Clic +Name[et]=Klõps +Name[eu]=Klikatu +Name[fa]=فشار +Name[fi]=Napsauta +Name[fr]=Clic +Name[ga]=Cliceáil +Name[he]=לחיצה +Name[hr]=Klik +Name[hu]=Kattintás +Name[is]=Smella +Name[it]=Clic +Name[ja]=クリック +Name[km]=ចុច +Name[ko]=클릭 +Name[lt]=Paspausti +Name[lv]=Klikšķis +Name[mk]=Кликање +Name[nb]=Klikk +Name[nds]=Klick +Name[ne]=क्लिक गर्नुहोस् +Name[nl]=Klik +Name[nn]=Klikk +Name[pa]=ਦਬਾਓ +Name[pl]=Dobierz +Name[pt]=Carregar +Name[pt_BR]=Clicar +Name[ru]=Щелчок +Name[se]=Coahkkal +Name[sk]=Kliknutie +Name[sl]=Klik +Name[sr]=Кликни +Name[sr@Latn]=Klikni +Name[sv]=Klick +Name[ta]=சொடுக்கு +Name[tg]=Ангуштзанӣ +Name[tr]=Tık +Name[uk]=Клац +Name[zh_CN]=单击 +Name[zh_TW]=點選 +Comment=Click +Comment[be]=Пстрычка +Comment[bg]=Щракване +Comment[bn]=ক্লিক +Comment[br]=Klik +Comment[bs]=Klik +Comment[ca]=Clic +Comment[cs]=Kliknutí +Comment[cy]=Clicio +Comment[da]=Klik +Comment[de]=Klick +Comment[el]=Κλικ +Comment[eo]=Kliki +Comment[es]=Pulsación +Comment[et]=Klõps +Comment[eu]=Klikatu +Comment[fa]=فشار +Comment[fi]=Napsauta +Comment[fr]=Clic +Comment[ga]=Cliceáil +Comment[he]=לחיצה +Comment[hr]=Klik +Comment[hu]=Kattintás +Comment[is]=Smella +Comment[it]=Clic +Comment[ja]=クリック +Comment[km]=ចុច +Comment[lt]=Paspausti +Comment[lv]=Klikšķis +Comment[mk]=Вртење +Comment[nb]=Klikk +Comment[nds]=Klick +Comment[ne]=क्लिक गर्नुहोस् +Comment[nl]=Klik +Comment[nn]=Klikk +Comment[pa]=ਦਬਾਓ +Comment[pl]=Kliknięcie +Comment[pt]=Carregar +Comment[pt_BR]=Clicar +Comment[ru]=Щелчок +Comment[se]=Coahkkal +Comment[sk]=Kliknutie +Comment[sl]=Klik +Comment[sr]=Кликни +Comment[sr@Latn]=Klikni +Comment[sv]=Klick +Comment[ta]=சொடுக்கு +Comment[tg]=Ангушт задан +Comment[tr]=Tık +Comment[uk]=Клац +Comment[zh_CN]=单击 +Comment[zh_TW]=點選 +default_presentation=1 +default_sound=reversi-click.wav + +[won] +Name=Game won +Name[ar]=ربحت اللعبة +Name[be]=Перамога +Name[bg]=Спечелихте +Name[bn]=খেলা জিতেছেন +Name[br]=Gounezet eo ar c'hoari +Name[bs]=Pobjeda +Name[ca]=Partida guanyada +Name[cs]=Vyhraná hra +Name[cy]=Gêm wedi ei ennill +Name[da]=Spillet vundet +Name[de]=Spiel gewonnen +Name[el]=Παιχνίδι κερδήθηκε +Name[eo]=Ludo venkita +Name[es]=Partida ganada +Name[et]=Mäng läbi, sina võitsid +Name[eu]=Jokoa irabazi da +Name[fa]=برد بازی +Name[fi]=Peli voitettu +Name[fr]=Partie gagnée +Name[gl]=Xogo gañado +Name[he]=ניצחת! +Name[hi]=खेल में जीत हुई +Name[hr]=Igra je dobivena +Name[hu]=Győzelem +Name[is]=Leikur unninn +Name[it]=Partita vinta +Name[ja]=ゲームに勝ち +Name[km]=ល្បែងបានឈ្នះ +Name[ko]=게임에서 이김 +Name[lt]=Žaidimas laimėtas +Name[lv]=Spēle uzvarēta +Name[mk]=Играта е добиена +Name[nb]=Du vant +Name[nds]=Speel wunnen +Name[ne]=खेल जित्नु भयो +Name[nl]=Spel gewonnen +Name[nn]=Du vann +Name[pa]=ਖੇਡ ਜਿੱਤੀ +Name[pl]=Gra wygrana +Name[pt]=Jogo ganho +Name[pt_BR]=Jogo ganho +Name[ro]=Joc cîştigat +Name[ru]=Победа +Name[se]=Don vuitet +Name[sk]=Vyhraná hra +Name[sl]=Igra je dobljena +Name[sr]=Игра је добијена +Name[sr@Latn]=Igra je dobijena +Name[sv]=Du vann spelet +Name[ta]=ஆட்டம் ஜெயிக்கப்பட்டது +Name[tg]=Дар бозӣ ғолиб омадед +Name[tr]=Oyun kazanıldı +Name[uk]=Гру виграно +Name[wa]=Djeu wangnî +Name[zh_CN]=您赢了游戏 +Name[zh_TW]=遊戲獲勝 +Comment=Game won +Comment[ar]=ربحت اللعبة +Comment[be]=Перамога +Comment[bg]=Спечелихте +Comment[bn]=খেল খতম +Comment[br]=Gounezet eo ar c'hoari +Comment[bs]=Pobjeda +Comment[ca]=Partida guanyada +Comment[cs]=Vyhraná hra +Comment[cy]=Gêm wedi ei ennill +Comment[da]=Spil vundet +Comment[de]=Spiel gewonnen +Comment[el]=Παιχνίδι κερδήθηκε +Comment[eo]=Ludo venkita +Comment[es]=Partida ganada +Comment[et]=Mäng läbi, sina võitsid +Comment[eu]=Jokoa irabazi da +Comment[fa]=برد بازی +Comment[fi]=Peli voitettu +Comment[fr]=Partie gagnée +Comment[gl]=Xogo gañado +Comment[he]=ניצחת! +Comment[hi]=खेल में जीत हुई +Comment[hr]=Igra je dobivena +Comment[hu]=Győzelem +Comment[is]=Leikur unninn +Comment[it]=Partita vinta +Comment[ja]=ゲームに勝ち +Comment[km]=ល្បែងបានឈ្នះ +Comment[ko]=게임에서 이김 +Comment[lt]=Žaidimas laimėtas +Comment[lv]=Spēle ir uzvarēta +Comment[mk]=Играта е добиена +Comment[nb]=Du vant! +Comment[nds]=Speel wunnen +Comment[ne]=खेल जित्नु भयो +Comment[nl]=Spel gewonnen +Comment[nn]=Du vann +Comment[pa]=ਖੇਡ ਜਿੱਤੀ +Comment[pl]=Gra wygrana +Comment[pt]=Jogo ganho +Comment[pt_BR]=Jogo ganho +Comment[ro]=Joc cîştigat +Comment[ru]=Победа +Comment[se]=Don vuitet +Comment[sk]=Vyhraná hra +Comment[sl]=Igra je dobljena +Comment[sr]=Игра је добијена +Comment[sr@Latn]=Igra je dobijena +Comment[sv]=Du vann spelet +Comment[ta]=ஆட்டம் ஜெயிக்கப்பட்டது +Comment[tg]=Дар бозӣ ғолиб омадед +Comment[tr]=Oyun kazanıldı +Comment[uk]=Гру виграно +Comment[wa]=Djeu wangnî +Comment[zh_CN]=您赢了游戏 +Comment[zh_TW]=遊戲獲勝 +default_presentation=1 +default_sound=reversi-won.wav + +[lost] +Name=Game lost +Name[ar]=خسرت اللعبة +Name[be]=Параза +Name[bg]=Загубихте +Name[bn]=খেলায় হেরে গিয়েছেন +Name[br]=Kollet eo ar c'hoari +Name[bs]=Poraz +Name[ca]=Partida perduda +Name[cs]=Prohraná hra +Name[cy]=Gêm wedi ei golli +Name[da]=Spil tabt +Name[de]=Spiel verloren +Name[el]=Παιχνίδι χάθηκε +Name[eo]=Ludo malvenkita +Name[es]=Partida perdida +Name[et]=Mäng läbi, sina kaotasid +Name[eu]=Jokoa galdu da +Name[fa]=باخت بازی +Name[fi]=Peli hävitty +Name[fr]=Partie perdue +Name[gl]=Xogo perdido +Name[he]=המשחק הסתיים, הפסדת +Name[hi]=खेल में हार हुई +Name[hr]=Igra je izgubljena +Name[hu]=Vereség +Name[is]=Leik tapað +Name[it]=Partita persa +Name[ja]=ゲームに負け +Name[km]=ល្បែងបានចាញ់ +Name[ko]=게임에서 짐 +Name[lt]=Žaidimas pralaimėtas +Name[lv]=Spēle zaudēta +Name[mk]=Играта е изгубена +Name[nb]=Du tapte +Name[nds]=Speel verloren +Name[ne]=खेल हार्नु भयो +Name[nl]=Spel verloren +Name[nn]=Du tapte +Name[pa]=ਖੇਡ ਹਾਰੀ +Name[pl]=Koniec gry, przegrałeś +Name[pt]=Jogo perdido +Name[pt_BR]=Jogo perdido +Name[ro]=Joc pierdut +Name[ru]=Поражение +Name[se]=Don vuoittohallet +Name[sk]=Prehraná hra +Name[sl]=Igra je izgubljena +Name[sr]=Игра је изгубљена +Name[sr@Latn]=Igra je izgubljena +Name[sv]=Du förlorade spelet +Name[ta]=ஆட்டம் இழக்கப்பட்டது +Name[tg]=Дар бозӣ мағлуб шудед +Name[tr]=Oyun kaybedildi +Name[uk]=Гра програна +Name[wa]=Djeu pierdou +Name[zh_CN]=您输了游戏 +Name[zh_TW]=遊戲失敗 +Comment=Game lost +Comment[ar]=خسرت اللعبة +Comment[be]=Параза +Comment[bg]=Загубихте +Comment[bn]=খেলায় হেরে গিয়েছেন +Comment[br]=Koll eo ar c'hoari +Comment[bs]=Poraz +Comment[ca]=Partida perduda +Comment[cs]=Prohraná hra +Comment[cy]=Gêm wedi ei golli +Comment[da]=Spil tabt +Comment[de]=Spiel verloren +Comment[el]=Παιχνίδι χάθηκε +Comment[eo]=Ludo malvenkita +Comment[es]=Partida perdida +Comment[et]=Mäng läbi, sina kaotasid +Comment[eu]=Jokoa galdu da +Comment[fa]=باخت بازی +Comment[fi]=Peli hävitty +Comment[fr]=Partie perdue +Comment[gl]=Xogo perdido +Comment[he]=המשחק הסתיים, הפסדת +Comment[hi]=खेल में हार हुई +Comment[hr]=Igra je izgubljena +Comment[hu]=Vereség +Comment[is]=Leik tapað +Comment[it]=Partita persa +Comment[ja]=ゲームに負け +Comment[km]=ល្បែងបានចាញ់ +Comment[ko]=게임에서 짐 +Comment[lt]=Žaidimas pralaimėtas +Comment[lv]=Spēle ir zaudēta +Comment[mk]=Играта е изубена +Comment[nb]=Du tapte +Comment[nds]=Speel verloren +Comment[ne]=खेल हार्नु भयो +Comment[nl]=Spel verloren +Comment[nn]=Du tapte +Comment[pa]=ਖੇਡ ਹਾਰੀ +Comment[pl]=Koniec gry, przegrałeś +Comment[pt]=Jogo perdido +Comment[pt_BR]=Jogo perdido +Comment[ro]=Joc pierdut +Comment[ru]=Поражение +Comment[se]=Don vuoittohallet +Comment[sk]=Prehraná hra +Comment[sl]=Igra je izgubljena +Comment[sr]=Игра је изгубљена +Comment[sr@Latn]=Igra je izgubljena +Comment[sv]=Du förlorade spelet +Comment[ta]=ஆட்டம் இழக்கப்பட்டது +Comment[tg]=Дар бозӣ мағлуб шудед +Comment[tr]=Oyun kaybedildi +Comment[uk]=Гра програна +Comment[wa]=Djeu pierdou +Comment[zh_CN]=您输了游戏 +Comment[zh_TW]=遊戲失敗 +default_presentation=0 + +[draw] +Name=Draw +Name[be]=Нічыя +Name[bg]=Равенство +Name[bn]=অমীমাংসিত +Name[br]=Tresañ +Name[bs]=Crtaj +Name[ca]=Empat +Name[cs]=Remíza +Name[cy]=Arlunio +Name[da]=Træk +Name[de]=Ziehen +Name[el]=Ισοπαλία +Name[eo]=Egaliĝo +Name[es]=Dibujar +Name[et]=Viik +Name[eu]=Berdinketa +Name[fa]=قرعهکشی +Name[fi]=Tasapeli +Name[fr]=Égalité +Name[ga]=Tarraing +Name[he]=תיקו +Name[hr]=Crtanje +Name[hu]=Döntetlen +Name[is]=Jafntefli +Name[it]=Pari +Name[ja]=引き分け +Name[km]=គូស +Name[ko]=비김 +Name[lt]=Piešti +Name[lv]=Vilkt +Name[mk]=Нерешено +Name[nb]=Uavgjort +Name[nds]=Trecken +Name[ne]=कोर्नुहोस् +Name[nl]=Tekenen +Name[nn]=Uavgjort +Name[pa]=ਬਰਾਬਰ +Name[pl]=Rysuj +Name[pt]=Empate +Name[pt_BR]=Empate +Name[ru]=Ничья +Name[sk]=Remíza +Name[sl]=Poteg +Name[sr]=Вуци +Name[sr@Latn]=Vuci +Name[sv]=Oavgjort +Name[ta]=வரை +Name[tg]=Кашидан +Name[tr]=Berabere +Name[uk]=Нічия +Name[zh_CN]=平局 +Name[zh_TW]=平手 +Comment=Draw +Comment[be]=Нічыя +Comment[bg]=Равенство +Comment[bn]=অমীমাংসিত +Comment[br]=Tresañ +Comment[bs]=Crtaj +Comment[ca]=Empat +Comment[cs]=Remíza +Comment[cy]=Arlunio +Comment[da]=Træk +Comment[de]=Ziehen +Comment[el]=Ισοπαλία +Comment[eo]=Egaliĝo +Comment[es]=Dibujar +Comment[et]=Viik +Comment[eu]=Berdinketa +Comment[fa]=قرعهکشی +Comment[fi]=Tasapeli +Comment[fr]=Égalité +Comment[ga]=Tarraing +Comment[he]=תיקו +Comment[hr]=Crtanje +Comment[hu]=Döntetlen +Comment[is]=Jafntefli +Comment[it]=Pari +Comment[ja]=引き分け +Comment[km]=គូស +Comment[lt]=Piešti +Comment[lv]=Vilkt +Comment[mk]=Нерешено +Comment[nb]=Uavgjort +Comment[nds]=Trecken +Comment[ne]=कोर्नुहोस् +Comment[nl]=Tekenen +Comment[nn]=Uavgjort +Comment[pa]=ਬਰਾਬਰ +Comment[pl]=Dobranie karty +Comment[pt]=Empate +Comment[pt_BR]=Empate +Comment[ru]=Ничья +Comment[sk]=Remíza +Comment[sl]=Poteg +Comment[sr]=Вуци +Comment[sr@Latn]=Vuci +Comment[sv]=Oavgjort +Comment[ta]=வரை +Comment[tg]=Кашидан +Comment[tr]=Beraberlik +Comment[uk]=нічия +Comment[zh_CN]=平局 +Comment[zh_TW]=平手 +default_presentation=0 + +[illegal_move] +Name=Illegal Move +Name[be]=Няправільны ход +Name[bg]=Невалиден ход +Name[bn]=অবৈধ চাল +Name[bs]=Nedozvoljen potez +Name[ca]=Moviment il·legal +Name[cs]=Neplatný tah +Name[cy]=Symudiad Angyfreithlon +Name[da]=Ulovligt træk +Name[de]=Unerlaubter Zug +Name[el]=Μη έγκυρη κίνηση +Name[eo]=Nepermesita movo +Name[es]=Movimiento ilegal +Name[et]=Lubamatu käik +Name[eu]=Legez kanpoko mugimendua +Name[fa]=حرکت غیر مجاز +Name[fi]=Laiton siirto +Name[fr]=Déplacement interdit +Name[ga]=Beart Neamhcheadaithe +Name[he]=מהלך לא חוקי +Name[hr]=Nepravilan potez +Name[hu]=Szabálytalan lépés +Name[is]=Ógildur leikur +Name[it]=Mossa non lecita +Name[ja]=不正な移動 +Name[km]=ការផ្លាស់ទីមិនត្រឹមត្រូវ +Name[ko]=잘못된 움직임 +Name[lt]=Negalimas ėjimas +Name[lv]=Nelegāls gājiens +Name[mk]=Недозволен потег +Name[nb]=Ugyldig trekk +Name[nds]=Tog nich verlöövt +Name[ne]=अवैध चाल +Name[nl]=Foutieve zet +Name[nn]=Ugyldig trekk +Name[pa]=ਗਲਤ ਚਾਲ +Name[pl]=Nieprawidłowy ruch +Name[pt]=Jogada Inválida +Name[pt_BR]=Movimento Ilegal +Name[ru]=Неправильный ход +Name[se]=Gustohis sirdin +Name[sk]=Neplatný ťah +Name[sl]=Neveljavna poteza +Name[sr]=Недозвољен потез +Name[sr@Latn]=Nedozvoljen potez +Name[sv]=Felaktigt drag +Name[ta]=நிகழ்ச்சியை வெளிப்படுத்து +Name[tg]=Ҳаракати Нодуруст +Name[tr]=Gecersiz Hamle +Name[uk]=Недозволений хід +Name[zh_CN]=无效移动 +Name[zh_TW]=不合法的移動 +Comment=Illegal move +Comment[be]=Няправільны ход +Comment[bg]=Невалиден ход +Comment[bn]=অবৈধ চাল +Comment[bs]=Nedozvoljen potez +Comment[ca]=Moviment il·legal +Comment[cs]=Neplatný tah +Comment[cy]=Symudiad angyfreithlon +Comment[da]=Ulovligt træk +Comment[de]=Unerlaubter Zug +Comment[el]=Μη έγκυρη κίνηση +Comment[eo]=Nepermesita movo +Comment[es]=Movimiento ilegal +Comment[et]=Lubamatu käik +Comment[eu]=Legez kanpoko mugimendua +Comment[fa]=حرکت غیر مجاز +Comment[fi]=Laiton siirto +Comment[fr]=Déplacement interdit +Comment[ga]=Beart neamhcheadaithe +Comment[he]=מהלך לא חוקי +Comment[hr]=Nepravilan potez +Comment[hu]=Szabálytalan lépés +Comment[is]=Ógildur leikur +Comment[it]=Mossa non lecita +Comment[ja]=不正な移動 +Comment[km]=ការផ្លាស់ទីមិនត្រឹមត្រូវ +Comment[ko]=잘못된 움직임 +Comment[lt]=Negalimas ėjimas +Comment[lv]=Šāds gājiens nav atļauts +Comment[mk]=Недозволен потег +Comment[nb]=Ugyldig trekk +Comment[nds]=Tog nich verlöövt +Comment[ne]=अवैध चाल +Comment[nl]=Foutieve zet +Comment[nn]=Ugyldig trekk +Comment[pa]=ਗਲਤ ਚਾਲ +Comment[pl]=Nieprawidłowy ruch +Comment[pt]=Jogada inválida +Comment[pt_BR]=Movimento ilegal +Comment[ru]=Неправильный ход +Comment[se]=Gustohis sirdin +Comment[sk]=Neplatný ťah +Comment[sl]=Neveljavna poteza +Comment[sr]=Недозвољен потез +Comment[sr@Latn]=Nedozvoljen potez +Comment[sv]=Felaktigt drag +Comment[ta]=தவறான நகர்த்தல் +Comment[tr]=Geçersiz hamle +Comment[uk]=Недозволений хід +Comment[zh_CN]=无效移动 +Comment[zh_TW]=不合法的移動 +default_presentation=0 diff --git a/kreversi/sounds/reversi-click.wav b/kreversi/sounds/reversi-click.wav Binary files differnew file mode 100644 index 00000000..0b3a2964 --- /dev/null +++ b/kreversi/sounds/reversi-click.wav diff --git a/kreversi/sounds/reversi-won.wav b/kreversi/sounds/reversi-won.wav Binary files differnew file mode 100644 index 00000000..8f5c0703 --- /dev/null +++ b/kreversi/sounds/reversi-won.wav diff --git a/kreversi/version.h b/kreversi/version.h new file mode 100644 index 00000000..d95b83ed --- /dev/null +++ b/kreversi/version.h @@ -0,0 +1 @@ +#define KREVERSI_VERSION "1.7.1" |