[kde-doc-english] [knavalbattle] src: implement the possibility of non adjacent ships

Jaime Torres jtamate at gmail.com
Sun Mar 10 09:37:50 UTC 2013


Git commit 411cdc90ee801825f8b96b9d1e87b225b90f117e by Jaime Torres.
Committed on 10/03/2013 at 10:25.
Pushed by jtamate into branch 'master'.

implement the possibility of non adjacent ships

Before, the only game option was to play with adjacent ships.
Now it is possible to choose between the two options, adjacent or not,
including the network games.
The game mode can not be changed in the middle of a battle.
The local user settings are not affected by a network game. Both
players will play the same game type, but after that, the local options
will apply for new local games.

FEATURE: 168659
FIXED-IN: 11.0
REVIEW: 106772
GUI: There is a new settings menu entry (Adjacent ships)

M  +38   -6    src/battlefield.cpp
M  +5    -5    src/battlefield.h
M  +3    -2    src/controller.cpp
M  +6    -5    src/controller.h
M  +4    -0    src/kbattleship.kcfg
M  +5    -1    src/kbattleshipui.rc
M  +6    -0    src/mainwindow.cpp
M  +9    -0    src/message.cpp
M  +39   -23   src/message.h
M  +20   -0    src/networkentity.cpp
M  +7    -1    src/playfield.cpp
M  +2    -1    src/playfield.h
M  +14   -1    src/protocol.cpp
M  +9    -3    src/sea.cpp
M  +7    -5    src/sea.h
M  +30   -0    src/ship.cpp

http://commits.kde.org/knavalbattle/411cdc90ee801825f8b96b9d1e87b225b90f117e

diff --git a/src/battlefield.cpp b/src/battlefield.cpp
index 32aa525..690bf93 100644
--- a/src/battlefield.cpp
+++ b/src/battlefield.cpp
@@ -12,6 +12,7 @@
 #include <kdebug.h>
 
 #include "sea.h"
+#include "settings.h"
 
 BattleField::BattleField(Sea* parent, const Coord& size)
 : QObject(parent)
@@ -87,16 +88,47 @@ void BattleField::addBorder(const Coord& pos)
     }
 }
 
-bool BattleField::canAddShip(const Coord& pos, unsigned int size, Ship::Direction direction) const
+bool BattleField::canAddShip(const Coord& pos, unsigned int size, Ship::Direction direction, const bool allow_adjacent_ships) const
 {
     Coord p = pos;
     Coord inc = Ship::increment(direction);
+    // Can not place a ship outside the battlefield
     for (unsigned int i = 0; i < size; i++) {
-        if (!valid(p))
-            return false;
-        if (!get(p).water())
-            return false;
-        p += inc;
+       if (!valid(p))
+          return false;
+       p += inc;
+    }
+    // nor over another ship
+    if (allow_adjacent_ships) {
+        p = pos;
+        for (unsigned int i = 0; i < size; i++) {
+            if (valid(p) && !get(p).water())
+                return false;
+            p += inc;
+        }
+    }
+    else {
+    // if not addjacent ships enabled, there must be
+    // a space between the already placed ships
+    // and the new ship
+        p=pos + Ship::decrement(direction) + Ship::decrementPerpendicular(direction);
+        for (unsigned int i = 0; i < size+2; i++) {
+            if (valid(p) && !get(p).water())
+                return false;
+            p += inc;
+        }
+        p=pos + Ship::decrement(direction);
+        for (unsigned int i = 0; i < size+2; i++) {
+            if (valid(p) && !get(p).water())
+                return false;
+            p += inc;
+        }
+        p=pos + Ship::decrement(direction) + Ship::incrementPerpendicular(direction);
+        for (unsigned int i = 0; i < size+2; i++) {
+            if (valid(p) && !get(p).water())
+                return false;
+            p += inc;
+        }
     }
     return true;
 }
diff --git a/src/battlefield.h b/src/battlefield.h
index ae78166..83d8f5f 100644
--- a/src/battlefield.h
+++ b/src/battlefield.h
@@ -24,27 +24,27 @@ Q_OBJECT
     Coord m_size;
     Board m_board;
     unsigned int m_ships;
-    
+
     inline int convert(const Coord& c) const { return c.x + m_size.x * c.y; }
 public:
     BattleField(Sea* parent, const Coord& size);
     ~BattleField();
-    
+
     bool valid(const Coord& pos) const;
     Element& get(const Coord& pos);
     const Element& get(const Coord& pos) const;
     void set(const Coord& pos, const Element& e);
-    
+
     void add(const Coord& pos, Ship* ship);
     void add(int n);
     void addBorder(const Coord& pos);
-    bool canAddShip(const Coord& pos, unsigned int size, Ship::Direction direction) const;
+    bool canAddShip(const Coord& pos, unsigned int size, Ship::Direction direction, const bool allow_adjacent_ships) const;
     HitInfo hit(const Coord& pos);
     void forceHit(const Coord& pos, const HitInfo& info);
     const Element& at(const Coord& c) const;
     Coord find(Ship* ship) const;
     bool isNearShip(const Coord& c) const;
-    
+
     inline unsigned int ships() const { return m_ships; }
 signals:
     void shipDestroyed(Ship*);
diff --git a/src/controller.cpp b/src/controller.cpp
index 761eda4..23b8153 100644
--- a/src/controller.cpp
+++ b/src/controller.cpp
@@ -17,15 +17,16 @@
 #include "shot.h"
 #include "audioplayer.h"
 
-Controller::Controller(QObject* parent, AudioPlayer* player)
+Controller::Controller(QObject* parent, AudioPlayer* player, const bool allow_adjacent_ships)
 : QObject(parent)
 , m_shot(0)
 , m_ready(0)
 , m_player(player)
 , m_has_ai(false)
+, m_allow_adjacent_ships(allow_adjacent_ships)
 {
     m_ui = 0;
-    m_sea = new Sea(this, Coord(10, 10));
+    m_sea = new Sea(this, Coord(10, 10), allow_adjacent_ships);
 }
 
 PlayerEntity* Controller::createPlayer(Sea::Player player, SeaView* view,
diff --git a/src/controller.h b/src/controller.h
index bbae7a6..e87ba48 100644
--- a/src/controller.h
+++ b/src/controller.h
@@ -33,23 +33,24 @@ Q_OBJECT
     int m_ready;
     AudioPlayer* m_player;
     bool m_has_ai;
-    
+    bool m_allow_adjacent_ships;
+
     void notify(Sea::Player player, const Coord& c, const HitInfo& info);
     void setupEntity(Entity*);
     void finalizeShot(Sea::Player player, const Coord& c, const HitInfo& info);
     void finalizeGame(Sea::Player winner);
     bool allPlayers() const;
 
-    
+
     friend class Shot;
 public:
-    explicit Controller(QObject* parent, AudioPlayer* player = 0);
+    explicit Controller(QObject* parent, AudioPlayer* player = 0, const bool allow_adjacent_ships = false);
 
-    PlayerEntity* createPlayer(Sea::Player player, SeaView* view, 
+    PlayerEntity* createPlayer(Sea::Player player, SeaView* view,
                                ChatWidget* chat, const QString& nick);
     AIEntity* createAI(Sea::Player player);
     NetworkEntity* createRemotePlayer(Sea::Player player, Protocol* protocol, bool client);
-    
+
     bool start(SeaView* view, bool ask = false);
     Entity* findEntity(Sea::Player) const;
     Sea::Player turn() const;
diff --git a/src/kbattleship.kcfg b/src/kbattleship.kcfg
index b615f30..bc6212b 100644
--- a/src/kbattleship.kcfg
+++ b/src/kbattleship.kcfg
@@ -22,5 +22,9 @@
       <label>Whether sound effects should be played.</label>
       <default>false</default>
     </entry>
+    <entry name="AdjacentShips" type="Bool">
+      <label>Allow the ships to be adjacent without one empty space between them.</label>
+      <default>true</default>
+    </entry>
   </group>
 </kcfg>
diff --git a/src/kbattleshipui.rc b/src/kbattleshipui.rc
index 7c51ac6..5f4f951 100644
--- a/src/kbattleshipui.rc
+++ b/src/kbattleshipui.rc
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gui name="kbattleship"
-     version="3"
+     version="4"
      xmlns="http://www.kde.org/standards/kxmlgui/1.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.kde.org/standards/kxmlgui/1.0
@@ -17,6 +17,7 @@
   <Menu name="settings">
     <Action name="options_nickname" />
     <Action name="options_sounds" />
+    <Action name="options_adjacent" />
     <Action name="options_show_endgame_message" />
     <Action name="options_showleftgrid" />
     <Action name="options_showrightgrid" />
@@ -36,6 +37,9 @@
     <enable>
         <Action name="game_restart"/>
     </enable>
+    <disable>
+        <Action name="options_adjacent"/>
+    </disable>
 </State>
 
 </gui>
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 82d9a14..e3f2a35 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -68,12 +68,18 @@ void MainWindow::setupActions()
     action = new KAction(KIcon( QLatin1String( SimpleMenu::iconClient) ), i18n("&Connect to Game..."), this);
     actionCollection()->addAction("game_create_client", action);
     connect(action, SIGNAL(triggered()), m_main, SLOT(createClient()));
+    // settings
     action = new KAction(i18n("Change &Nickname..."), this);
     actionCollection()->addAction("options_nickname", action);
     connect(action, SIGNAL(triggered()), m_main, SLOT(changeNick()));
     action = new KToggleAction(i18n("&Play Sounds"), this);
     actionCollection()->addAction("options_sounds", action);
     connect(action, SIGNAL(triggered(bool)), m_main, SLOT(toggleSounds(bool)));
+    // This action will be disabled when a game is being run
+    action = new KToggleAction(i18n("&Adjacent Ships"), this);
+    action->setChecked(Settings::adjacentShips());
+    actionCollection()->addAction("options_adjacent", action);
+    connect(action, SIGNAL(triggered(bool)), m_main, SLOT(toggleAdjacent(bool)));
     // config end of game message
     action = new KToggleAction(i18n("Show End-of-Game Message"), this);
     action->setChecked(true);
diff --git a/src/message.cpp b/src/message.cpp
index 59de527..dad3c83 100644
--- a/src/message.cpp
+++ b/src/message.cpp
@@ -117,4 +117,13 @@ void GameOverMessage::accept(MessageVisitor& visitor) const
     visitor.visit(*this);
 }
 
+GameOptionsMessage::GameOptionsMessage(const QString& enableAdjacentShips, const QString& oneOrSeveralShips)
+: m_enabledAdjacentShipsString(enableAdjacentShips)
+, m_oneOrSeveralShipsString(oneOrSeveralShips)
+{
+}
 
+void GameOptionsMessage::accept(MessageVisitor& visitor) const
+{
+    visitor.visit(*this);
+}
diff --git a/src/message.h b/src/message.h
index 2757cf7..eb4c997 100644
--- a/src/message.h
+++ b/src/message.h
@@ -105,26 +105,6 @@ public:
     const Coord& stop() const { return m_stop; }
 };
 
-class ChatMessage : public Message
-{
-    QString m_nickname;
-    QString m_chat;
-public:
-    static const int MSGTYPE = 8;
-    explicit ChatMessage(const QString& nick, const QString& chat);
-    virtual void accept(MessageVisitor& visitor) const;
-    
-    const QString& chat() const { return m_chat; }
-    const QString& nickname() const { return m_nickname; }
-};
-
-class RestartMessage : public Message
-{
-public:
-    static const int MSGTYPE = 7;
-    virtual void accept(MessageVisitor& visitor) const;
-};
-
 class GameOverMessage : public Message
 {
 public:
@@ -133,7 +113,7 @@ public:
         Coord pos;
         int size;
         Ship::Direction direction;
-        
+
         ShipInfo(const Coord& pos, int size, Ship::Direction direction)
         : pos(pos)
         , size(size)
@@ -146,13 +126,48 @@ private:
 public:
     static const int MSGTYPE = 6;
     GameOverMessage();
-    
+
     void addShip(const Coord& pos, int size, Ship::Direction direction);
     virtual void accept(MessageVisitor& visitor) const;
-    
+
     const QList<ShipInfo>& ships() const { return m_ships; }
 };
 
+class RestartMessage : public Message
+{
+public:
+    static const int MSGTYPE = 7;
+    virtual void accept(MessageVisitor& visitor) const;
+};
+
+class ChatMessage : public Message
+{
+    QString m_nickname;
+    QString m_chat;
+public:
+    static const int MSGTYPE = 8;
+    explicit ChatMessage(const QString& nick, const QString& chat);
+    virtual void accept(MessageVisitor& visitor) const;
+
+    const QString& chat() const { return m_chat; }
+    const QString& nickname() const { return m_nickname; }
+};
+
+
+class GameOptionsMessage : public Message
+{
+private:
+    QString m_enabledAdjacentShipsString;
+    QString m_oneOrSeveralShipsString;
+public:
+    static const int MSGTYPE = 9;
+    GameOptionsMessage(const QString& enableAdjacentShips, const QString& oneOrSeveralShips);
+
+    const QString & enabledAdjacentShips() const { return m_enabledAdjacentShipsString; }
+    const QString & oneOrSeveralShips() const { return m_oneOrSeveralShipsString; }
+    virtual void accept(MessageVisitor& visitor) const;
+};
+
 
 class MessageVisitor
 {
@@ -167,6 +182,7 @@ public:
     virtual void visit(const GameOverMessage& msg) = 0;
     virtual void visit(const RestartMessage& msg) = 0;
     virtual void visit(const ChatMessage& msg) = 0;
+    virtual void visit(const GameOptionsMessage& msg) = 0;
 };
 
 #endif // MESSAGE_H
diff --git a/src/networkentity.cpp b/src/networkentity.cpp
index 5fff5b8..9f93e85 100644
--- a/src/networkentity.cpp
+++ b/src/networkentity.cpp
@@ -12,12 +12,16 @@
 #include "battlefield.h"
 #include "shot.h"
 #include "protocol.h"
+#include "settings.h"
 
 #include <KIcon>
+#include <klocalizedstring.h>
+#include <kdebug.h>
 
 NetworkEntity::NetworkEntity(Sea::Player player, Sea* sea, Protocol* protocol, bool client)
 : Entity(player)
 , m_sea(sea)
+, m_pending_shot(0)
 , m_client(client)
 {
     m_protocol = protocol;
@@ -36,6 +40,8 @@ void NetworkEntity::start(bool ask)
     }
     else {
         m_protocol->send(MessagePtr(new HeaderMessage()));
+
+        m_protocol->send(MessagePtr(new GameOptionsMessage(QString(Settings::adjacentShips() ? "true" : "false"), /* TODO */"true")));
     }
 }
 
@@ -198,6 +204,20 @@ void NetworkEntity::visit(const ChatMessage& msg)
     emit chat(msg.chat());
 }
 
+void NetworkEntity::visit(const GameOptionsMessage& msg)
+{
+    bool enabledAdjacentShips = (msg.enabledAdjacentShips() == QString("true"));
+    m_sea->allowAdjacentShips( enabledAdjacentShips );
+
+    if (enabledAdjacentShips) {
+        emit chat(i18n("You can place ships adjacent to each other"));
+    }
+    else {
+        emit chat(i18n("You must leave an space between ships"));
+    }
+}
+
+
 KIcon NetworkEntity::icon() const
 {
     return KIcon( QLatin1String( "network-workgroup" ));
diff --git a/src/playfield.cpp b/src/playfield.cpp
index 61c0ac6..15f4e96 100644
--- a/src/playfield.cpp
+++ b/src/playfield.cpp
@@ -67,7 +67,7 @@ PlayField::~PlayField()
 
 Controller* PlayField::createController()
 {
-    Controller* controller = new Controller(this, m_player);
+    Controller* controller = new Controller(this, m_player, Settings::adjacentShips());
     connect(controller, SIGNAL(gameOver(Sea::Player)),
             this, SLOT(gameOver(Sea::Player)));
     connect(controller, SIGNAL(restartRequested()),
@@ -230,6 +230,12 @@ void PlayField::toggleSounds(bool enable)
     m_player->setActive(enable);
 }
 
+void PlayField::toggleAdjacent(bool enable)
+{
+    Settings::setAdjacentShips(enable);
+    Settings::self()->writeConfig();
+}
+
 void PlayField::restartRequested()
 {
     int ans = KMessageBox::questionYesNo(this, i18n("Restart game"),
diff --git a/src/playfield.h b/src/playfield.h
index 56934dc..6a24657 100644
--- a/src/playfield.h
+++ b/src/playfield.h
@@ -45,11 +45,12 @@ public slots:
     void highscores();
     void gameOver(Sea::Player winner);
     void setupController();
-    
+
     void newGame();
     void restart(bool ask = true);
     void changeNick();
     void toggleSounds(bool);
+    void toggleAdjacent(bool);
     void restartRequested();
     void setCompatibility(int);
     void updateNick(int, const QString&);
diff --git a/src/protocol.cpp b/src/protocol.cpp
index c5cbacc..152c9cd 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -42,7 +42,7 @@ public:
         m_doc.appendChild(m_main);
     }
     
-    QDomDocument document() { return m_doc; }
+    QDomDocument document() const { return m_doc; }
 
     virtual void visit(const HeaderMessage& msg)
     {
@@ -108,6 +108,13 @@ public:
         ADD_FIELD(msg, chat);
         ADD_FIELD(msg, nickname);
     }
+
+    virtual void visit(const GameOptionsMessage& msg)
+    {
+        setType(msg);
+        ADD_FIELD(msg, enabledAdjacentShips);
+        ADD_FIELD(msg, oneOrSeveralShips);
+    }
 };
 
 
@@ -235,6 +242,12 @@ MessagePtr Protocol::parseMessage(const QString& xmlMessage)
             DEF_ELEMENT(chat);
             return MessagePtr(new ChatMessage(nickname, chat));
         }
+    case GameOptionsMessage::MSGTYPE:
+        {
+            DEF_ELEMENT(enabledAdjacentShips);
+            DEF_ELEMENT(oneOrSeveralShips);
+            return MessagePtr(new GameOptionsMessage(enabledAdjacentShips, oneOrSeveralShips));
+        }
     default:
         emit parseError("Unknown message type");
         return MessagePtr();
diff --git a/src/sea.cpp b/src/sea.cpp
index de8b697..51f4f0c 100644
--- a/src/sea.cpp
+++ b/src/sea.cpp
@@ -1,6 +1,6 @@
 /*
   Copyright (c) 2007 Paolo Capriotti <p.capriotti at gmail.com>
-            
+
   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
@@ -10,11 +10,12 @@
 #include "sea.h"
 #include "battlefield.h"
 
-Sea::Sea(QObject* parent, const Coord& size)
+Sea::Sea(QObject* parent, const Coord& size, const bool allow_adjacent_ships)
 : QObject(parent)
 , m_size(size)
 , m_turn(PLAYER_A)
 , m_status(PLACING_SHIPS)
+, m_allow_adjacent_ships(allow_adjacent_ships)
 {
     m_fields[0] = new BattleField(this, m_size);
     m_fields[1] = new BattleField(this, m_size);
@@ -31,7 +32,7 @@ bool Sea::canAddShip(Player p, const Coord& pos, int size, Ship::Direction direc
     if (m_status != PLACING_SHIPS) {
         return false;
     }
-    return m_fields[p]->canAddShip(pos, size, direction);
+    return m_fields[p]->canAddShip(pos, size, direction, m_allow_adjacent_ships);
 }
 
 void Sea::add(Player p, const Coord& pos, Ship* ship)
@@ -84,6 +85,11 @@ void Sea::checkGameOver()
     }
 }
 
+void Sea::allowAdjacentShips(const bool allow_adjacent_ships)
+{
+    m_allow_adjacent_ships = allow_adjacent_ships;
+}
+
 const Element& Sea::at(Sea::Player player, const Coord& c) const
 {
     return m_fields[player]->at(c);
diff --git a/src/sea.h b/src/sea.h
index e76be24..c68f348 100644
--- a/src/sea.h
+++ b/src/sea.h
@@ -38,15 +38,16 @@ private:
     Player m_turn;
     BattleField* m_fields[2];
     Status m_status;
-    
+    bool m_allow_adjacent_ships;
+
     inline BattleField* currentField() const { return m_fields[m_turn]; }
     inline BattleField* otherField() const { return m_fields[opponent(m_turn)]; }
-    
+
     void checkGameOver();
 public:
-    Sea(QObject* parent, const Coord& size);
+    Sea(QObject* parent, const Coord& size, const bool allow_adjacent_ships);
     ~Sea();
-    
+
     bool canAddShip(Player p, const Coord& pos, int size, Ship::Direction direction) const;
     void add(Player p, int n);
     void add(Player p, const Coord& pos, Ship* ship);
@@ -60,7 +61,8 @@ public:
     bool valid(Sea::Player, const Coord& pos) const;
     void switchTurn();
     bool isNearShip(Sea::Player, const Coord& pos) const;
-    
+    void allowAdjacentShips(const bool allow_adjacent_ships);
+
     inline Status status() const { return m_status; }
     inline Player turn() const { return m_turn; }
     static Player opponent(Player p);
diff --git a/src/ship.cpp b/src/ship.cpp
index 86858d9..cf78535 100644
--- a/src/ship.cpp
+++ b/src/ship.cpp
@@ -27,11 +27,41 @@ Coord Ship::increment(Direction direction)
     return direction == TOP_DOWN ? Coord(0, 1) : Coord(1, 0);
 }
 
+Coord Ship::decrement(Direction direction)
+{
+    return direction == TOP_DOWN ? Coord(0, -1) : Coord(-1, 0);
+}
+
+Coord Ship::incrementPerpendicular(Direction direction)
+{
+    return direction == TOP_DOWN ? Coord(1, 0) : Coord(0, 1);
+}
+
+Coord Ship::decrementPerpendicular(Direction direction)
+{
+    return direction == TOP_DOWN ? Coord(-1, 0) : Coord(0, -1);
+}
+
 Coord Ship::increment() const
 {
     return increment(m_direction);
 }
 
+Coord Ship::decrement() const
+{
+    return decrement(m_direction);
+}
+
+Coord Ship::incrementPerpendicular() const
+{
+    return incrementPerpendicular(m_direction);
+}
+
+Coord Ship::decrementPerpendicular() const
+{
+    return decrementPerpendicular(m_direction);
+}
+
 void Ship::decLife()
 {
     Q_ASSERT(alive());


More information about the kde-doc-english mailing list