/* Class AbTop */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "AbTop.h" #include "Board.h" #include "BoardWidget.h" #include "EvalDlgImpl.h" #include "EvalScheme.h" #include "Network.h" #include "Spy.h" #include "version.h" #include #include // #define MYTRACE 1 const AbTop::Data AbTop::LEVEL[Nb_Levels] = { { "Easy", I18N_NOOP("&Easy") }, { "Normal", I18N_NOOP("&Normal") }, { "Hard", I18N_NOOP("&Hard") }, { "Challange", I18N_NOOP("&Challenge") } }; const AbTop::Data AbTop::IPLAY[Nb_IPlays] = { { "Red", I18N_NOOP("&Red") }, { "Yellow", I18N_NOOP("&Yellow") }, { "Both", I18N_NOOP("&Both") }, { "None", I18N_NOOP("&None") } }; AbTop::AbTop() :TDEMainWindow(0) { timerState = noGame; myPort = Network::defaultPort; currentEvalScheme = 0; net = 0; actValue = 0; stop = false; editMode = false; spyLevel = 0; pastePossible = true; timer = new TQTimer; connect( timer, TQT_SIGNAL(timeout()), TQT_TQOBJECT(this), TQT_SLOT(timerDone()) ); board = new Board(); setMoveNo(0); connect( board, TQT_SIGNAL(searchBreak()), TQT_TQOBJECT(this), TQT_SLOT(searchBreak()) ); TQ_CHECK_PTR(board); boardWidget = new BoardWidget(*board,this); #ifdef SPION spy = new Spy(*board); #endif connect( boardWidget, TQT_SIGNAL(updateSpy(TQString)), TQT_TQOBJECT(this), TQT_SLOT(updateSpy(TQString)) ); setCentralWidget(boardWidget); boardWidget->show(); // this creates the GUI setupActions(); setupStatusBar(); setMinimumSize(200,300); // RMB context menu connect( boardWidget, TQT_SIGNAL(rightButtonPressed(int,const TQPoint&)), TQT_TQOBJECT(this), TQT_SLOT(rightButtonPressed(int,const TQPoint&)) ); connect( boardWidget, TQT_SIGNAL(edited(int)), TQT_TQOBJECT(this), TQT_SLOT(edited(int)) ); connect( board, TQT_SIGNAL(updateBestMove(Move&,int)), TQT_TQOBJECT(this), TQT_SLOT(updateBestMove(Move&,int)) ); connect( boardWidget, TQT_SIGNAL(moveChoosen(Move&)), TQT_TQOBJECT(this), TQT_SLOT(moveChoosen(Move&)) ); /* default */ setLevel(Easy); setIPlay(Red); showMoveLong = true; showSpy = false; renderBalls = true; updateStatus(); updateActions(); setupGUI(); } AbTop::~AbTop() { /* Unregister from other abalone processes */ delete net; delete timer; #ifdef SPION delete spy; #endif } /** * Create all the actions... * * The GUI will be built in createGUI using a XML file * */ void AbTop::setupActions() { newAction = KStdGameAction::gameNew( TQT_TQOBJECT(this), TQT_SLOT(newGame()), actionCollection() ); KStdGameAction::quit( TQT_TQOBJECT(this), TQT_SLOT(close()), actionCollection() ); stopAction = new TDEAction( i18n("&Stop Search"), "process-stop", Key_S, TQT_TQOBJECT(this), TQT_SLOT(stopSearch()), actionCollection(), "move_stop"); backAction = new TDEAction( i18n("Take &Back"), "back", TDEStdAccel::shortcut(TDEStdAccel::Prior), TQT_TQOBJECT(this), TQT_SLOT(back()), actionCollection(), "move_back"); forwardAction = new TDEAction( i18n("&Forward"), "forward", TDEStdAccel::shortcut(TDEStdAccel::Next), TQT_TQOBJECT(this), TQT_SLOT(forward()), actionCollection(), "move_forward"); hintAction = KStdGameAction::hint(TQT_TQOBJECT(this), TQT_SLOT(suggestion()), actionCollection()); KStdAction::copy( TQT_TQOBJECT(this), TQT_SLOT(copy()), actionCollection()); pasteAction = KStdAction::paste( TQT_TQOBJECT(this), TQT_SLOT(paste()), actionCollection()); (void) new TDEAction( i18n("&Restore Position"), TDEStdAccel::shortcut(TDEStdAccel::Open), TQT_TQOBJECT(this), TQT_SLOT(restorePosition()), actionCollection(), "edit_restore" ); (void) new TDEAction( i18n("&Save Position"), TDEStdAccel::shortcut(TDEStdAccel::Save), TQT_TQOBJECT(this), TQT_SLOT(savePosition()), actionCollection(), "edit_save" ); TDEToggleAction *ta; ta = new TDEToggleAction( i18n("&Network Play"), "network", Key_N, actionCollection(), "game_net"); connect(ta, TQT_SIGNAL(toggled(bool)), TQT_TQOBJECT(this), TQT_SLOT(gameNetwork(bool))); editAction = new TDEToggleAction( i18n("&Modify"), "edit", CTRL+Key_Insert, actionCollection(), "edit_modify"); connect(editAction, TQT_SIGNAL(toggled(bool)), TQT_TQOBJECT(this), TQT_SLOT( editModify(bool))); showMenubar = KStdAction::showMenubar(TQT_TQOBJECT(this), TQT_SLOT(toggleMenubar()), actionCollection()); KStdAction::saveOptions( TQT_TQOBJECT(this), TQT_SLOT(writeConfig()), actionCollection()); KStdAction::preferences( TQT_TQOBJECT(this), TQT_SLOT(configure()), actionCollection()); moveSlowAction = new TDEToggleAction( i18n("&Move Slow"), 0, actionCollection(), "options_moveSlow"); connect(moveSlowAction, TQT_SIGNAL(toggled(bool)), TQT_TQOBJECT(this), TQT_SLOT(optionMoveSlow(bool))); renderBallsAction = new TDEToggleAction( i18n("&Render Balls"), 0, actionCollection(), "options_renderBalls"); connect(renderBallsAction, TQT_SIGNAL(toggled(bool)), TQT_TQOBJECT(this), TQT_SLOT(optionRenderBalls(bool))); showSpyAction = new TDEToggleAction( i18n("&Spy"), 0, actionCollection(), "options_showSpy"); connect(showSpyAction, TQT_SIGNAL(toggled(bool)), TQT_TQOBJECT(this), TQT_SLOT(optionShowSpy(bool))); levelAction = KStdGameAction::chooseGameType(0, 0, actionCollection()); TQStringList list; for (uint i=0; isetItems(list); connect(levelAction, TQT_SIGNAL(activated(int)), TQT_SLOT(setLevel(int))); iplayAction = new TDESelectAction(i18n("&Computer Play"), 0, actionCollection(), "options_iplay"); list.clear(); for (uint i=0; isetItems(list); connect(iplayAction, TQT_SIGNAL(activated(int)), TQT_SLOT(setIPlay(int))); } void AbTop::toggleMenubar() { if (menuBar()->isVisible()) menuBar()->hide(); else menuBar()->show(); } void AbTop::configure() { KDialogBase *dlg = new KDialogBase( 0, "ConfigureEvaluation", true, i18n("Configure Evaluation"), KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, true); EvalDlgImpl *edlg = new EvalDlgImpl(dlg,board); dlg->setMainWidget(edlg); if (dlg->exec()) { *currentEvalScheme = *(edlg->evalScheme()); board->setEvalScheme(currentEvalScheme); } delete edlg; } /* Right Mouse button pressed in BoardWidget area */ void AbTop::rightButtonPressed(int /* field */, const TQPoint& pos) { TQPopupMenu* rmbMenu = static_cast (factory()->container("rmbPopup",this)); if (rmbMenu) rmbMenu->popup( pos ); } /* Read config options * * menu must already be created! */ void AbTop::readConfig() { kdDebug(12011) << "Reading config..." << endl; TDEConfig* config = kapp->config(); config->setGroup("Options"); readOptions(config); applyMainWindowSettings( config, "Appearance" ); showMenubar->setChecked( !menuBar()->isHidden() ); currentEvalScheme = new EvalScheme("Current"); currentEvalScheme->read(config); board->setEvalScheme( currentEvalScheme ); } void AbTop::readOptions(TDEConfig* config) { TQString entry = config->readEntry("Level"); for (uint i=0; ireadEntry("Computer"); for (uint i=0; ireadBoolEntry("MoveSlow", false); moveSlowAction->setChecked( showMoveLong ); renderBalls = config->readBoolEntry("RenderBalls", true); boardWidget->renderBalls(renderBalls); renderBallsAction->setChecked( renderBalls ); showSpy = config->readBoolEntry("ShowSpy", true); board->updateSpy(showSpy); showSpyAction->setChecked( showSpy ); } void AbTop::readProperties(TDEConfig *config) { TQString entry; readOptions(config); currentEvalScheme = new EvalScheme("Current"); currentEvalScheme->read(config); board->setEvalScheme( currentEvalScheme ); if (!(entry = config->readEntry("TimerState")).isNull()) timerState = entry.toInt(); if (timerState == noGame) return; stop = config->readBoolEntry("GameStopped", false); int mNo = 0; if (!(entry = config->readEntry("Position")).isNull()) { mNo = board->setState(entry); boardWidget->updatePosition(true); } setMoveNo(mNo, true); show(); playGame(); } void AbTop::writeConfig() { kdDebug(12011) << "Writing config..." << endl; TDEConfig* config = kapp->config(); config->setGroup("Options"); writeOptions(config); saveMainWindowSettings( config, "Appearance" ); if (currentEvalScheme) currentEvalScheme->save(config); config->sync(); } void AbTop::writeOptions(TDEConfig *config) { config->writeEntry("Level", LEVEL[levelAction->currentItem()].key); config->writeEntry("Computer", IPLAY[iplayAction->currentItem()].key); config->writeEntry("MoveSlow", showMoveLong); config->writeEntry("RenderBalls", renderBalls); config->writeEntry("ShowSpy", showSpy); } void AbTop::saveProperties(TDEConfig *config) { writeOptions(config); if (currentEvalScheme) currentEvalScheme->save(config); config->writeEntry("TimerState", timerState); if (timerState == noGame) return; config->writeEntry("GameStopped", stop); config->writeEntry("Position", board->getState(moveNo)); config->sync(); } void AbTop::savePosition() { TDEConfig* config = kapp->config(); config->setGroup("SavedPosition"); config->writeEntry("Position", board->getState(moveNo)); } void AbTop::restorePosition() { TDEConfig* config = kapp->config(); config->setGroup("SavedPosition"); TQString entry = config->readEntry("Position"); timerState = notStarted; timer->stop(); board->begin(Board::color1); stop = false; setMoveNo( board->setState(entry), true ); if (net) net->broadcast( board->getASCIIState( moveNo ).ascii() ); boardWidget->updatePosition(true); playGame(); } void AbTop::setupStatusBar() { TQString tmp; TQString t = i18n("Press %1 for a new game").arg( newAction->shortcut().toString()); statusLabel = new TQLabel( t, statusBar(), "statusLabel" ); statusBar()->addWidget(statusLabel,1,false); // PERMANENT: Moving side + move No. // validPixmap, only visible in Modify mode: is position valid ? warningPix = BarIcon( "warning" ); okPix = BarIcon( "ok" ); validLabel = new TQLabel( "", statusBar(), "validLabel" ); validLabel->setFixedSize( 18, statusLabel->sizeHint().height() ); validLabel->setAlignment( AlignCenter ); validLabel->hide(); validShown = false; redBall = BarIcon( "redball" ); yellowBall = BarIcon( "yellowball" ); noBall = BarIcon( "noball" ); ballLabel = new TQLabel( "", statusBar(), "ballLabel" ); ballLabel->setPixmap(noBall); ballLabel->setFixedSize( 18, statusLabel->sizeHint().height() ); ballLabel->setAlignment( AlignCenter ); statusBar()->addWidget(ballLabel, 0, true); moveLabel = new TQLabel( i18n("Move %1").arg("--"), statusBar(), "moveLabel" ); statusBar()->addWidget(moveLabel, 0, true); #ifdef MYTRACE /* Create a toolbar menu for debugging output level */ TDEToolBar *tb = toolBar("mainToolBar"); if (tb) { TQPopupMenu* spyPopup = new TQPopupMenu; spy0 = BarIcon( "spy0" ); spy1 = BarIcon( "spy1" ); spy2 = BarIcon( "spy2" ); spy3 = BarIcon( "spy3" ); spyPopup->insertItem(spy0, 0); spyPopup->insertItem(spy1, 1); spyPopup->insertItem(spy2, 2); spyPopup->insertItem(spy3, 3); connect( spyPopup, TQT_SIGNAL(activated(int)), TQT_TQOBJECT(this), TQT_SLOT(setSpy(int)) ); tb->insertButton(spy0, 30, spyPopup, TRUE, i18n("Spy")); } #endif } void AbTop::updateSpy(TQString s) { if (showSpy) { if (s.isEmpty()) { updateStatus(); // statusBar()->clear(); } else statusLabel->setText(s); } } void AbTop::updateBestMove(Move& m, int value) { if (showSpy) { boardWidget->showMove(m,3); boardWidget->showMove(m,0,false); TQString tmp; tmp.sprintf("%s : %+d", (const char*) m.name().utf8(), value-actValue); updateSpy(tmp); kapp->processEvents(); } } void AbTop::updateStatus() { TQString tmp; bool showValid = false; if (!editMode && timerState == noGame) { tmp = i18n("Move %1").arg("--"); ballLabel->setPixmap(noBall); } else { tmp = i18n("Move %1").arg(moveNo/2 + 1); ballLabel->setPixmap( (board->actColor() == Board::color1) ? redBall : yellowBall); } moveLabel->setText(tmp); if (editMode) { tmp = TQString("%1: %2 %3 - %4 %5") .arg( i18n("Edit") ) .arg( i18n("Red") ).arg(boardWidget->getColor1Count()) .arg( i18n("Yellow") ).arg(boardWidget->getColor2Count()); validLabel->setPixmap( (board->validState() == Board::invalid) ? warningPix:okPix ); showValid = true; } else if (timerState == noGame) { tmp = i18n("Press %1 for a new game").arg( newAction->shortcut().toString()); } else { if (timerState == gameOver) { tmp = (board->actColor() == Board::color2) ? i18n("Red won"):i18n("Yellow won"); validLabel->setPixmap( warningPix ); showValid = true; } else { tmp = TQString("%1 - %2") .arg( (board->actColor() == Board::color1) ? i18n("Red"):i18n("Yellow") ) .arg( iPlayNow() ? i18n("I am thinking...") : i18n("It is your turn!") ); } } statusLabel->setText(tmp); if (validShown != showValid) { if (showValid) { statusBar()->addWidget(validLabel); validLabel->show(); } else { statusBar()->removeWidget(validLabel); validLabel->hide(); } validShown = showValid; } statusBar()->clear(); statusBar()->repaint(); } void AbTop::edited(int vState) { if (vState == Board::empty) timerState = noGame; updateStatus(); } /* only , , have to be updated */ void AbTop::updateActions() { bool iPlay = iPlayNow(); /* New && Copy always on */ /* Paste */ pastePossible = !iPlay; pasteAction->setEnabled(!iPlay); /* Edit */ editAction->setEnabled(!iPlay); /* Stop search */ stopAction->setEnabled(iPlay); /* Back */ bool bBack = (editMode && moveNo>0) || (board->movesStored() >=1 && !iPlay); backAction->setEnabled(bBack); /* Forward */ bool bForward = editMode && moveNo<999; forwardAction->setEnabled(bForward); /* Hint */ bool bHint = !editMode && !iPlay && (haveHint().type != Move::none); hintAction->setEnabled(bHint); } /* let the program be responsive even in a long search... */ void AbTop::searchBreak() { kapp->processEvents(); } void AbTop::setSpy(int id ) { toolBar("mainToolBar")->setButtonPixmap(30, (id==0)?spy0:(id==1)?spy1:(id==2)?spy2:spy3 ); spyLevel = id; board->setSpyLevel(spyLevel); } void AbTop::timerDone() { int interval = 400; switch(timerState) { case noGame: case notStarted: return; case showMove: case showMove+2: case showSugg: case showSugg+2: case showSugg+4: boardWidget->showMove(actMove, 2); interval = 200; break; case showMove+1: case showMove+3: case showSugg+1: case showSugg+3: boardWidget->showMove(actMove, 3); break; case showSugg+5: interval = 800; case showMove+4: boardWidget->showMove(actMove, 4); break; case showMove+5: boardWidget->showMove(actMove, 0); timerState = moveShown; playGame(); return; case showSugg+6: boardWidget->showMove(actMove, 0); timerState = notStarted; return; } timerState++; timer->start(interval,TRUE); } void AbTop::userMove() { /* User has to move */ static MoveList list; list.clear(); board->generateMoves(list); if (list.getLength() == 0) { stop = true; timerState = gameOver; playGame(); } else boardWidget->choseMove(&list); } bool AbTop::iPlayNow() { if (editMode || (board->validState() != Board::valid) || timerState == gameOver) return false; int c = board->actColor(); /* color1 is red */ return ((iplay == Both) || ((c == Board::color1) && (iplay == Red) ) || ((c == Board::color2) && (iplay == Yellow) )); } void AbTop::playGame() { if (timerState == moveShown) { if (actMove.type != Move::none) { board->playMove(actMove); moveNo++; // actColor in board is changed in playMove if (net) net->broadcast( board->getASCIIState( moveNo ).ascii() ); } actValue = - board->calcEvaluation(); boardWidget->updatePosition(true); timerState = notStarted; } if (!board->isValid()) { stop = true; timerState = gameOver; } updateStatus(); updateActions(); boardWidget->setCursor(crossCursor); if (stop) return; if (!iPlayNow()) { userMove(); return; } boardWidget->setCursor(waitCursor); kapp->processEvents(); if (moveNo <4) { /* Chose a random move making the position better for actual color */ /* If comparing ratings among color1/2 on move, we have to negate one */ int v = -board->calcEvaluation(), vv; do { actMove = board->randomMove(); board->playMove(actMove); vv = board->calcEvaluation(); board->takeBack(); } while( (board->actColor() == Board::color1) ? (vvv) ); } else { actMove = (board->bestMove()); if (actMove.type == Move::none) { stop = true; timerState = gameOver; playGame(); return; } } timerState = showMoveLong ? showMove : showMove+3; timerDone(); } void AbTop::moveChoosen(Move& m) { actMove = m; timerState = moveShown; playGame(); } void AbTop::newGame() { /* stop a running animation */ timerState = notStarted; timer->stop(); /* reset board */ board->begin(Board::color1); boardWidget->updatePosition(true); setMoveNo(0, true); if (net) net->broadcast( board->getASCIIState( moveNo ).ascii() ); /* if not in EditMode, start Game immediately */ if (!editMode) { stop = false; playGame(); } } /* Copy ASCII representation into Clipboard */ void AbTop::copy() { TQClipboard *cb = TQApplication::clipboard(); cb->setText( board->getASCIIState( moveNo ).ascii() ); } void AbTop::paste() { if (!pastePossible) return; TQClipboard *cb = TQApplication::clipboard(); pastePosition( cb->text().ascii() ); /* don't do this in pastePosition: RECURSION !! */ if (net) net->broadcast( board->getASCIIState( moveNo ).ascii() ); } void AbTop::pastePosition(const char * text) { if (!pastePossible) return; if ( text ) { timerState = notStarted; timer->stop(); board->begin(Board::color1); stop = false; int mNo = board->setASCIIState(text); if (mNo<0) mNo=0; setMoveNo( mNo, true); boardWidget->updatePosition(true); if ( (board->validState()==Board::invalid) && !editMode) { editAction->setChecked(true); return; } playGame(); } } void AbTop::gameNetwork(bool on) { if (!on) { if (net != 0) { delete net; net = 0; } return; } if (myPort == 0) myPort = Network::defaultPort; net = new Network(myPort); char *h, h2[100]; int p, i; for(h = hosts.first(); h!=0; h=hosts.next()) { for(i=0;h[i]!=0 && h[i]!=':';i++); if (h[i]==':') p = atoi(h+i+1); else p = 0; if (p == 0) p = Network::defaultPort; strncpy(h2,h,i); h2[i]=0; net->addListener(h2, p); } TQObject::connect(net, TQT_SIGNAL(gotPosition(const char *)), TQT_TQOBJECT(this), TQT_SLOT(pastePosition(const char *)) ); } void AbTop::editModify(bool on) { int vState = board->validState(); editMode = boardWidget->setEditMode( on ); if (vState != Board::valid) timerState = noGame; updateActions(); updateStatus(); if (!editMode && vState == Board::valid) { actMove.type = Move::none; timerState = moveShown; playGame(); } } void AbTop::stopGame() { stop = true; board->stopSearch(); } void AbTop::stopSearch() { // When computer plays both, switch back to human for next color if (iplay == Both) { if (board->actColor() == Board::color1) setIPlay(Red); else setIPlay(Yellow); } board->stopSearch(); } bool AbTop::queryClose() { board->stopSearch(); return true; } void AbTop::continueGame() { if (timerState != noGame && timerState != gameOver) { stop = false; if (timerState == notStarted) playGame(); } } /** * Reset the Move number of the actual game to * If is true, update GUI actions and redraw statusbar */ void AbTop::setMoveNo(int m, bool updateGUI) { moveNo = m; board->setActColor( ((moveNo%2)==0) ? Board::color1 : Board::color2 ); if (updateGUI) { updateStatus(); updateActions(); } } /* "Back" action activated * * If in edit mode, simple go 1 back * If in a game, go back 2 if possible */ void AbTop::back() { if (editMode) { if (moveNo > 0) setMoveNo(moveNo-1, true); return; } if (moveNo < 1) return; if (timerState == gameOver) timerState = notStarted; if (timerState != notStarted) return; /* If possible, go 2 steps back */ if (moveNo>0 && board->takeBack()) moveNo--; if (moveNo>0 && board->takeBack()) moveNo--; setMoveNo( moveNo, true ); boardWidget->updatePosition(true); userMove(); } /* Only for edit Mode */ void AbTop::forward() { if (editMode) { if (moveNo < 999) setMoveNo(moveNo+1, true); return; } } Move AbTop::haveHint() { static Move m; static int oldMoveNo = 0; if (timerState != notStarted) { m.type = Move::none; } else if (moveNo != oldMoveNo) { MoveList list; oldMoveNo = moveNo; m = board->nextMove(); board->generateMoves(list); if (!list.isElement(m,0)) m.type = Move::none; } return m; } void AbTop::suggestion() { if (timerState != notStarted) return; Move m = haveHint(); if (m.type == Move::none) return; actMove = m; timerState = showSugg; timerDone(); } void AbTop::setLevel(int l) { levelAction->setCurrentItem(l); depth = l+2; board->setDepth(depth); // kdDebug(12011) << "Level set to " << d << endl; } void AbTop::setIPlay(int i) { iplayAction->setCurrentItem(i); iplay = (IPlay)i; continueGame(); } void AbTop::optionMoveSlow(bool on) { showMoveLong = on; } void AbTop::optionRenderBalls(bool on) { renderBalls = on; boardWidget->renderBalls(renderBalls); } void AbTop::optionShowSpy(bool on) { showSpy = on; board->updateSpy(showSpy); #ifdef SPION if (showSpy) spy->show(); else { spy->nextStep(); spy->hide(); } #endif } #include "AbTop.moc"