File indexing completed on 2024-04-28 07:52:37

0001 /*
0002     SPDX-FileCopyrightText: 2007 Paolo Capriotti <p.capriotti@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "battlefield.h"
0008 
0009 #include "sea.h"
0010 #include "settings.h"
0011 
0012 BattleField::BattleField(Sea* parent, const Coord& size, const bool allow_adjacent_ships)
0013 : QObject(parent)
0014 , m_size(size)
0015 , m_board(size)
0016 , m_secondary_board(size)
0017 , m_allow_adjacent_ships(allow_adjacent_ships)
0018 , m_ships(0)
0019 {
0020     setUpSecondaryBoard();
0021 }
0022 
0023 void BattleField::setUpSecondaryBoard()
0024 {
0025     for (int i = 0; i < m_size.x; i++) {
0026         for (int j = 0; j < m_size.y ; j++) {
0027             m_secondary_board[Coord(i,j)] = false;
0028         }
0029     }
0030 }
0031 
0032 BattleField::~BattleField()
0033 {
0034     clear();
0035 }
0036 
0037 bool BattleField::valid(const Coord& pos) const
0038 {
0039     return m_board.valid(pos);
0040 }
0041 
0042 Element& BattleField::get(const Coord& pos)
0043 {
0044     return m_board[pos];
0045 }
0046 
0047 const Element& BattleField::get(const Coord& pos) const
0048 {
0049     return m_board[pos];
0050 }
0051 
0052 void BattleField::set(const Coord& pos, const Element& e)
0053 {
0054     if (valid(pos)) {
0055         get(pos) = e;
0056     }
0057 }
0058 
0059 void BattleField::add(int n)
0060 {
0061     m_ships += n;
0062 }
0063 
0064 void BattleField::add(Ship* ship)
0065 {
0066     Coord p = ship->position();
0067     for (unsigned int i = 0; i < ship->size(); i++) {
0068         set(p, Element(ship));
0069         p = p + ship->increment();
0070     }
0071     m_ships++;
0072 
0073     addSecondaryBoard(ship);
0074     addBorderSecondaryBoard(ship);
0075 }
0076 
0077 void BattleField::addBorder(const Coord& pos)
0078 {
0079     Ship* ship = get(pos).parent();
0080     if (ship) {
0081         Coord inc = ship->increment();
0082         Coord orth(inc.y, inc.x);
0083         Coord p = pos - inc;
0084         set(p, Element::BORDER);
0085         for (; p != pos + inc * (ship->size() + 1); p += inc) {
0086             set(p + orth, Element::BORDER);
0087             set(p - orth, Element::BORDER);
0088         }
0089         p -= inc;
0090         set(p, Element::BORDER);
0091     }
0092 }
0093 
0094 void BattleField::addSecondaryBoard(Ship* ship)
0095 {
0096     Coord p = ship->position();
0097     for (unsigned int i = 0; i < ship->size(); i++) {
0098         m_secondary_board[p]=true;
0099         p += ship->increment();
0100     }
0101 }
0102 
0103 void BattleField::addBorderSecondaryBoard(Ship* ship)
0104 {
0105     if ( !m_allow_adjacent_ships )
0106     {
0107         Coord inc = ship->increment();
0108         Coord orth(inc.y, inc.x);
0109         Coord p = ship->position() - inc;
0110         if ( valid (p) ) {
0111             m_secondary_board[p]=true;
0112         }
0113         for (; p != ship->position() + inc * (ship->size() + 1); p += inc) {
0114             if ( valid( p + orth ) ) {
0115                 m_secondary_board[p + orth]=true;
0116             }
0117             if ( valid( p - orth ) ) {
0118                 m_secondary_board[p - orth]=true;
0119             }
0120         }
0121         p -= inc;
0122         if ( valid (p) ) {
0123             m_secondary_board[p]=true;
0124         }
0125     }
0126 }
0127 
0128 bool BattleField::canAddShip(const Coord& pos, unsigned int size, Ship::Direction direction) const
0129 {
0130     Coord p = pos;
0131     Coord inc = Ship::increment(direction);
0132     // Can not place a ship outside the battlefield
0133     for (unsigned int i = 0; i < size; i++) {
0134        if (!valid(p))
0135           return false;
0136        p += inc;
0137     }
0138     // nor over another ship
0139     if (m_allow_adjacent_ships) {
0140         p = pos;
0141         for (unsigned int i = 0; i < size; i++) {
0142             if (valid(p) && !get(p).water())
0143                 return false;
0144             p += inc;
0145         }
0146     }
0147     else {
0148     // if not addjacent ships enabled, there must be
0149     // a space between the already placed ships
0150     // and the new ship
0151         p=pos + Ship::decrement(direction) + Ship::decrementPerpendicular(direction);
0152         for (unsigned int i = 0; i < size+2; i++) {
0153             if (valid(p) && !get(p).water())
0154                 return false;
0155             p += inc;
0156         }
0157         p=pos + Ship::decrement(direction);
0158         for (unsigned int i = 0; i < size+2; i++) {
0159             if (valid(p) && !get(p).water())
0160                 return false;
0161             p += inc;
0162         }
0163         p=pos + Ship::decrement(direction) + Ship::incrementPerpendicular(direction);
0164         for (unsigned int i = 0; i < size+2; i++) {
0165             if (valid(p) && !get(p).water())
0166                 return false;
0167             p += inc;
0168         }
0169     }
0170     return true;
0171 }
0172 
0173 bool BattleField::canAddShipOfSizeInHorizontal(unsigned int size) const
0174 {
0175     unsigned int maxLenAvailable = 0;
0176     for (int j=0; j<m_secondary_board.height(); j++) {
0177         unsigned int contiguousLen = 0;
0178         for (int i=0; i<m_secondary_board.width(); i++) {
0179             if ( m_secondary_board[ Coord(i,j) ] ) {
0180                 contiguousLen = 0;
0181             }
0182             else {
0183                 contiguousLen += 1;
0184                 maxLenAvailable = qMax<int>( contiguousLen, maxLenAvailable );
0185                 if (maxLenAvailable >= size)
0186                 {
0187                     return true;
0188                 }
0189             }
0190         }
0191     }
0192     return false;
0193 }
0194 
0195 bool BattleField::canAddShipOfSizeInVertical(unsigned int size) const
0196 {
0197     unsigned int maxLenAvailable = 0;
0198     for (int i=0; i<m_secondary_board.width(); i++) {
0199         unsigned int contiguousLen = 0;
0200         for (int j=0; j<m_secondary_board.height(); j++) {
0201             if ( m_secondary_board[ Coord(i,j) ] ) {
0202                 contiguousLen = 0;
0203             }
0204             else {
0205                 contiguousLen += 1;
0206                 maxLenAvailable = qMax<int>( contiguousLen, maxLenAvailable );
0207                 if (maxLenAvailable >= size)
0208                 {
0209                     return true;
0210                 }
0211             }
0212         }
0213     }
0214     return false;
0215 }
0216 
0217 bool BattleField::canAddShipOfSize(unsigned int size) const
0218 {
0219     return canAddShipOfSizeInHorizontal(size) || canAddShipOfSizeInVertical(size);
0220 }
0221 
0222 
0223 HitInfo BattleField::hit(const Coord& pos)
0224 {
0225     Element& e = get(pos);
0226     HitInfo::Type type = e.hit();
0227     Ship* ship = e.parent();
0228     
0229     HitInfo res(type);
0230     if (ship && !ship->alive())
0231     {
0232         m_ships--;
0233         res.shipDestroyed = ship;
0234         res.shipPos = find(ship);
0235     }
0236 
0237     return res;
0238 }
0239 
0240 void BattleField::forceHit(const Coord& pos, const HitInfo& info)
0241 {
0242     switch (info.type) {
0243     case HitInfo::HIT:
0244         get(pos).setType(Element::DEAD);
0245         if (info.shipDestroyed) {
0246             Coord c = info.shipPos;
0247             for (unsigned int i = 0; i < info.shipDestroyed->size(); i++) {
0248                 get(c).setParent(info.shipDestroyed);
0249                 c += info.shipDestroyed->increment();
0250             }
0251             m_ships--;
0252         }
0253         break;
0254     case HitInfo::MISS:
0255         get(pos).setType(Element::MISS);
0256         break;
0257     case HitInfo::INVALID:
0258         break;
0259     }
0260 }
0261 
0262 const Element& BattleField::at(const Coord& c) const
0263 {
0264     return m_board[c];
0265 }
0266 
0267 Coord BattleField::find(Ship* ship) const
0268 {
0269     FOREACH_SQUARE(p, m_board) {
0270         if (m_board[p].parent() == ship) {
0271             return p;
0272         }
0273     }
0274     return Coord::invalid();
0275 }
0276 
0277 bool BattleField::isNearShip(const Coord& pos) const
0278 {
0279     for (int i = -1; i <= 1; i++)
0280     for (int j = -1; j <= 1; j++) {
0281         Coord p = pos + Coord(i,j);
0282         if (valid(p) && get(p).parent()) {
0283             return true;
0284         }
0285     }
0286     return false;
0287 }
0288 
0289 void BattleField::clear()
0290 {
0291     for (int i = 0; i < m_size.x; i++) {
0292         for (int j = 0; j < m_size.y ; j++) {
0293             m_secondary_board[Coord(i,j)] = false;
0294         }
0295     }
0296     QSet<Ship*> deleted_ships;
0297     FOREACH_SQUARE(p, m_board) {
0298         Ship* ship = m_board[p].parent();
0299         if (ship && !deleted_ships.contains(ship)) {
0300             delete ship;
0301             deleted_ships.insert(ship);
0302         }
0303         m_board[p].setType(Element::WATER);
0304         m_board[p].setParent(nullptr);
0305     }
0306     m_ships=0;
0307 }
0308 
0309 #include "moc_battlefield.cpp"