File indexing completed on 2024-09-08 06:47:56
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"