File indexing completed on 2024-06-09 04:03:38
0001 /* Copyright (C) 2023 Stephan Kulow <coolo@kde.org> 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 */ 0004 0005 #include "spidersolver2.h" 0006 #include "../spider.h" 0007 #include <QDebug> 0008 #include <algorithm> 0009 #include <cstdlib> 0010 #include <cstring> 0011 #include <iostream> 0012 // #include <time.h> 0013 #include <unordered_map> 0014 #include <unordered_set> 0015 0016 namespace spidersolver2 0017 { 0018 static volatile bool stop_exec = false; 0019 0020 enum Suit { Clubs = 0, Diamonds, Hearts, Spades }; 0021 enum Rank { None = 0, Ace = 1, Two = 2, Three = 3, Four = 4, Five = 5, Six = 6, Seven = 7, Eight = 8, Nine = 9, Ten = 10, Jack = 11, Queen = 12, King = 13 }; 0022 0023 // give magic numbers a name 0024 const int REDEALS_NR = 6; 0025 const int MAX_MOVES = 230; 0026 0027 struct Card { 0028 // 4 bits rank 0029 // 2 bits suit 0030 // 1 bit faceup 0031 // 1 bit unknown 0032 unsigned char value; 0033 0034 Card() 0035 { 0036 value = 0; 0037 } 0038 0039 Card(unsigned char v) 0040 { 0041 value = v; 0042 } 0043 0044 Card(KCard *c) 0045 { 0046 value = 0; 0047 set_faceup(c->isFaceUp()); 0048 set_rank(Rank(c->rank())); 0049 set_suit(Suit(c->suit())); 0050 } 0051 0052 inline bool is_faceup() const 0053 { 0054 return (value & (1 << 6)) > 0; 0055 } 0056 0057 void set_faceup(bool face) 0058 { 0059 if (face) { 0060 value = value | (1 << 6); 0061 } else { 0062 value = value & ~(1 << 6); 0063 } 0064 } 0065 0066 inline bool is_unknown() const 0067 { 0068 return (value & (1 << 7)) > 0; 0069 } 0070 0071 void set_unknown(bool unknown) 0072 { 0073 if (unknown) { 0074 value = value | (1 << 7); 0075 } else { 0076 value = value & ~(1 << 7); 0077 } 0078 } 0079 0080 inline Rank rank() const 0081 { 0082 return Rank(value & 15); 0083 } 0084 0085 void set_rank(Rank rank) 0086 { 0087 value = (value & ~15) + rank; 0088 } 0089 0090 inline Suit suit() const 0091 { 0092 return Suit((value >> 4) & 3); 0093 } 0094 0095 void set_suit(Suit suit) 0096 { 0097 Rank _rank = rank(); 0098 value = value >> 4; 0099 value = (value & ~3) + suit; 0100 value = (value << 4) + _rank; 0101 } 0102 0103 bool inSequenceTo(const Card &other) const; 0104 std::string toString() const; 0105 unsigned char raw_value() const 0106 { 0107 return value; 0108 } 0109 bool operator==(const Card &rhs) const; 0110 }; 0111 0112 inline bool Card::inSequenceTo(const Card &other) const 0113 { 0114 return other.is_faceup() && other.suit() == suit() && other.rank() == rank() + 1; 0115 } 0116 0117 std::string Card::toString() const 0118 { 0119 if (is_unknown() && is_faceup()) 0120 return "XX"; 0121 if (is_unknown()) 0122 return "|XX"; 0123 0124 std::string ret; 0125 switch (rank()) { 0126 case Ace: 0127 ret += 'A'; 0128 break; 0129 case Jack: 0130 ret += 'J'; 0131 break; 0132 case Queen: 0133 ret += 'Q'; 0134 break; 0135 case King: 0136 ret += 'K'; 0137 break; 0138 case Ten: 0139 ret += 'T'; 0140 break; 0141 case None: 0142 ret += 'N'; 0143 break; 0144 default: 0145 if (rank() < 2 || rank() > 9) { 0146 printf("rank is out of range\n"); 0147 exit(1); 0148 } 0149 ret += ('0' + rank()); 0150 break; 0151 } 0152 switch (suit()) { 0153 case Spades: 0154 ret += "S"; 0155 break; 0156 case Hearts: 0157 ret += "H"; 0158 break; 0159 case Diamonds: 0160 ret += "D"; 0161 break; 0162 case Clubs: 0163 ret += "C"; 0164 break; 0165 default: 0166 std::cerr << "Invalid suit " << suit() << std::endl; 0167 exit(1); 0168 } 0169 if (!is_faceup()) 0170 ret = "|" + ret; 0171 return ret; 0172 } 0173 0174 // to remove known cards from partly unknown decks. We don't care for faceup and unknown 0175 bool Card::operator==(const Card &rhs) const 0176 { 0177 return suit() == rhs.suit() && rank() == rhs.rank(); 0178 } 0179 0180 struct Move { 0181 bool off; 0182 bool talon; 0183 unsigned char from; 0184 unsigned char to; 0185 unsigned char index; 0186 Move() 0187 { 0188 talon = false; 0189 off = false; 0190 from = -1; 0191 to = -1; 0192 index = 0; 0193 } 0194 Move(bool _off, bool _talon, int _from, int _to, int _index) 0195 { 0196 off = _off; 0197 talon = _talon; 0198 from = _from; 0199 to = _to; 0200 index = _index; 0201 } 0202 static Move fromTalon(int talon) 0203 { 0204 return Move(false, true, talon, 0, 0); 0205 } 0206 static Move toOff(int from, int index) 0207 { 0208 return Move(true, false, from, 0, index); 0209 } 0210 static Move regular(int from, int to, int index) 0211 { 0212 return Move(false, false, from, to, index); 0213 } 0214 MOVE to_MOVE() const; 0215 }; 0216 0217 MOVE Move::to_MOVE() const 0218 { 0219 MOVE ret; 0220 ret.card_index = index; 0221 ret.from = from; 0222 if (talon) { 0223 ret.from = 11; 0224 } 0225 ret.to = to; 0226 if (off) { 0227 ret.totype = O_Type; 0228 ret.pri = 127; 0229 } else { 0230 ret.totype = W_Type; 0231 } 0232 return ret; 0233 } 0234 0235 const int MAX_CARDS = 104; 0236 0237 class Pile; 0238 class Pile 0239 { 0240 friend class MemoryManager; 0241 0242 public: 0243 Pile() 0244 { 0245 count = 0; 0246 } 0247 const Pile *addCard(const Card &c) const; 0248 std::string toString() const; 0249 bool empty() const 0250 { 0251 return count == 0; 0252 } 0253 const Card at(int index) const 0254 { 0255 return Card(cards[index]); 0256 } 0257 0258 inline size_t cardCount() const 0259 { 0260 return count; 0261 } 0262 const Pile *remove(int index) const; 0263 const Pile *copyFrom(const Pile *from, int index) const; 0264 const Pile *replaceAt(int index, const Card &c) const; 0265 int chaos() const 0266 { 0267 return m_chaos; 0268 } 0269 void calculateChaos(); 0270 int sequenceOf(Suit suit) const 0271 { 0272 return m_seqs[suit]; 0273 } 0274 int playableCards() const; 0275 uint64_t hash() const 0276 { 0277 return m_hash; 0278 } 0279 0280 private: 0281 int m_chaos; 0282 unsigned char cards[MAX_CARDS]; 0283 size_t count; 0284 uint64_t m_hash; 0285 int m_seqs[4]; 0286 int sequenceOf_(Suit suit) const; 0287 void setAt(int index, const Card &c) 0288 { 0289 cards[index] = c.raw_value(); 0290 } 0291 }; 0292 0293 class Deck 0294 { 0295 private: 0296 // Deck(const Deck &other); 0297 0298 public: 0299 Deck() 0300 { 0301 // leaving the pointers stale on purpose 0302 // for performance 0303 } 0304 0305 std::string toString() const; 0306 void update(const Deck *); 0307 void translate(const Spider *dealer); 0308 void getMoves(std::vector<Move> &moves) const; 0309 void applyMove(const Move &m, Deck &newdeck) const; 0310 uint64_t id() const; 0311 int leftTalons() const; 0312 int chaos() const; 0313 SolverInterface::ExitStatus shortestPath(); 0314 int playableCards() const; 0315 int inOff() const; 0316 int freePlays() const; 0317 void makeEmpty(); 0318 std::vector<Card> parse(int game_type, const std::string &filename); 0319 std::vector<Move> getWinMoves() const; 0320 0321 private: 0322 const Pile *play[10]; 0323 const Pile *talon[5]; 0324 const Pile *off; 0325 Move moves[MAX_MOVES]; 0326 int moves_index; 0327 }; 0328 0329 class MemoryManager 0330 { 0331 public: 0332 MemoryManager(); 0333 ~MemoryManager(); 0334 const Pile *createEmptyPile(); 0335 const Pile *translatePile(const KCardPile *pile, bool reverse); 0336 const Pile *createPile(const unsigned char *cards, size_t count); 0337 Deck &deckAt(size_t index) 0338 { 0339 return deckstore[index]; 0340 } 0341 void clean(); 0342 static MemoryManager *self() 0343 { 0344 return m_self; 0345 } 0346 0347 private: 0348 static MemoryManager *m_self; 0349 std::unordered_map<uint64_t, Pile *> pilemap; 0350 Deck *deckstore; 0351 }; 0352 0353 const Pile *MemoryManager::createPile(const unsigned char *cards, size_t count) 0354 { 0355 std::hash<unsigned char> hasher; 0356 std::size_t seed = 0; 0357 for (size_t i = 0; i < count; i++) { 0358 seed ^= hasher(cards[i]) + 0x9e3779b9 + (seed << 6) + (seed >> 2); 0359 } 0360 auto search = pilemap.find(seed); 0361 if (search != pilemap.end()) 0362 return search->second; 0363 Pile *p = new Pile; 0364 memcpy(p->cards, cards, count); 0365 p->count = count; 0366 p->calculateChaos(); 0367 p->m_hash = seed; 0368 p->m_seqs[Hearts] = p->sequenceOf_(Hearts); 0369 p->m_seqs[Spades] = p->sequenceOf_(Spades); 0370 p->m_seqs[Clubs] = p->sequenceOf_(Clubs); 0371 p->m_seqs[Diamonds] = p->sequenceOf_(Diamonds); 0372 pilemap.insert({seed, p}); 0373 return p; 0374 } 0375 0376 std::string Pile::toString() const 0377 { 0378 std::string ret; 0379 for (size_t i = 0; i < count; i++) { 0380 ret += " " + Card(cards[i]).toString(); 0381 } 0382 return ret; 0383 } 0384 0385 const Pile *Pile::remove(int index) const 0386 { 0387 if (index > 0) { 0388 Card c = at(index - 1); 0389 if (!c.is_faceup()) { 0390 static unsigned char newcards[MAX_CARDS]; 0391 memcpy(newcards, cards, MAX_CARDS); 0392 c.set_faceup(true); 0393 newcards[index - 1] = c.raw_value(); 0394 return MemoryManager::self()->createPile(newcards, index); 0395 } else { 0396 return MemoryManager::self()->createPile(cards, index); 0397 } 0398 } else { 0399 return MemoryManager::self()->createPile(cards, 0); 0400 } 0401 } 0402 0403 const Pile *Pile::copyFrom(const Pile *from, int index) const 0404 { 0405 unsigned char newcards[MAX_CARDS]; 0406 memcpy(newcards, cards, count); 0407 size_t newcount = count; 0408 for (size_t i = index; i < from->count; i++) { 0409 newcards[newcount++] = from->cards[i]; 0410 } 0411 return MemoryManager::self()->createPile(newcards, newcount); 0412 } 0413 0414 const Pile *Pile::addCard(const Card &c) const 0415 { 0416 unsigned char newcards[MAX_CARDS]; 0417 memcpy(newcards, cards, MAX_CARDS); 0418 newcards[count] = c.raw_value(); 0419 return MemoryManager::self()->createPile(newcards, count + 1); 0420 } 0421 0422 void Pile::calculateChaos() 0423 { 0424 m_chaos = 0; 0425 Card lastcard; 0426 for (size_t i = 0; i < cardCount(); i++) { 0427 Card current = at(i); 0428 0429 // first in stack 0430 if (lastcard.raw_value() == 0) { 0431 m_chaos++; 0432 } else { 0433 if (!current.inSequenceTo(lastcard)) { 0434 m_chaos++; 0435 } 0436 } 0437 lastcard = current; 0438 } 0439 } 0440 0441 const Pile *Pile::replaceAt(int index, const Card &c) const 0442 { 0443 unsigned char newcards[MAX_CARDS]; 0444 memcpy(newcards, cards, MAX_CARDS); 0445 newcards[index] = c.raw_value(); 0446 return MemoryManager::self()->createPile(newcards, count); 0447 } 0448 0449 int Pile::sequenceOf_(Suit suit) const 0450 { 0451 int index = cardCount(); 0452 if (index == 0) { 0453 return index; 0454 } 0455 index--; 0456 Card top_card = at(index); 0457 if (top_card.suit() != suit) { 0458 return 0; 0459 } 0460 while (index > 0 && top_card.inSequenceTo(at(index - 1))) { 0461 index--; 0462 top_card = at(index); 0463 } 0464 return cardCount() - index; 0465 } 0466 0467 int Pile::playableCards() const 0468 { 0469 if (count < 2) { 0470 return count; 0471 } 0472 return sequenceOf(at(count - 1).suit()); 0473 } 0474 0475 struct WeightedDeck { 0476 unsigned char left_talons; 0477 unsigned char in_off; 0478 unsigned char free_plays; 0479 unsigned char playable; 0480 int32_t chaos; 0481 int64_t id; 0482 int32_t index; 0483 0484 bool operator<(const WeightedDeck &rhs) const; 0485 void update(uint64_t hash); 0486 }; 0487 0488 void WeightedDeck::update(uint64_t hash) 0489 { 0490 Deck &deck = MemoryManager::self()->deckAt(index); 0491 left_talons = deck.leftTalons(); 0492 id = hash; 0493 in_off = deck.inOff(); 0494 free_plays = deck.freePlays(); 0495 playable = deck.playableCards(); 0496 chaos = deck.chaos(); 0497 } 0498 0499 // smaller is better! 0500 bool WeightedDeck::operator<(const WeightedDeck &rhs) const 0501 { 0502 if (chaos != rhs.chaos) { 0503 // smaller chaos is better 0504 return chaos < rhs.chaos; 0505 } 0506 int ready1 = playable + in_off + free_plays; 0507 int ready2 = rhs.playable + rhs.in_off + rhs.free_plays; 0508 if (ready1 != ready2) { 0509 // larger values are better 0510 return ready1 > ready2; 0511 } 0512 0513 // once we are in straight win mode, we go differently 0514 if (chaos == 0) { 0515 int free1 = free_plays; 0516 int free2 = rhs.free_plays; 0517 0518 if (free1 != free2) { 0519 // more free is better 0520 return free1 > free2; 0521 } 0522 // if the number of empty plays is equal, less in the off 0523 // is actually a benefit (more strongly ordered) 0524 int off1 = in_off; 0525 int off2 = rhs.in_off; 0526 if (off1 != off2) { 0527 return off1 < off2; 0528 } 0529 } 0530 // give a reproducible sort order, but std::sort doesn't give 0531 // guarantess for equal items, so prefer them being different 0532 return id < rhs.id; 0533 } 0534 0535 std::string Deck::toString() const 0536 { 0537 std::string ret; 0538 char buffer[200]; 0539 int counter = 0; 0540 for (int i = 0; i < 10; i++) { 0541 snprintf(buffer, sizeof(buffer), "Play%d:", i); 0542 ret += buffer; 0543 ret += play[i]->toString(); 0544 ret += "\n"; 0545 } 0546 0547 for (int i = 0; i < 5; i++) { 0548 snprintf(buffer, sizeof(buffer), "Deal%d:", i); 0549 ret += buffer; 0550 0551 ret += talon[i]->toString(); 0552 ret += "\n"; 0553 counter++; 0554 } 0555 0556 ret += "Off:"; 0557 ret += off->toString(); 0558 ret += "\n"; 0559 0560 return ret; 0561 } 0562 0563 void Deck::getMoves(std::vector<Move> &moves) const 0564 { 0565 moves.clear(); 0566 if (moves_index >= MAX_MOVES - 1) { 0567 return; 0568 } 0569 0570 int next_talon = -1; 0571 for (int i = 0; i < 5; i++) { 0572 if (!talon[i]->empty()) { 0573 next_talon = i; 0574 break; 0575 } 0576 } 0577 0578 int from = 0; 0579 bool one_is_empty = false; 0580 for (; from < 10; from++) { 0581 if (play[from]->empty()) { 0582 one_is_empty = true; 0583 continue; 0584 } 0585 0586 int index = play[from]->cardCount() - 1; 0587 Suit top_suit = play[from]->at(index).suit(); 0588 int top_rank = int(play[from]->at(index).rank()) - 1; 0589 0590 while (index >= 0) { 0591 Card current = play[from]->at(index); 0592 if (!current.is_faceup()) 0593 break; 0594 if (current.suit() != top_suit) 0595 break; 0596 if (top_rank + 1 != current.rank()) 0597 break; 0598 top_rank = current.rank(); 0599 0600 if (play[from]->cardCount() - index == 13) { 0601 moves.clear(); 0602 moves.push_back(Move::toOff(from, index)); 0603 return; 0604 } 0605 int broken_sequence = 0; 0606 if (index > 0) { 0607 Card next_card = play[from]->at(index - 1); 0608 if (current.inSequenceTo(next_card)) { 0609 broken_sequence = play[from]->cardCount() - index; 0610 } 0611 } 0612 bool moved_to_empty = false; 0613 for (int to = 0; to < 10; to++) { 0614 if (to == from) 0615 continue; 0616 int to_count = play[to]->cardCount(); 0617 if (to_count > 0) { 0618 Card top_card = play[to]->at(to_count - 1); 0619 if (top_card.rank() != top_rank + 1) 0620 continue; 0621 // make sure we only enlarge sequences 0622 if (broken_sequence > 0) { 0623 if (play[to]->sequenceOf(top_suit) + broken_sequence <= play[from]->sequenceOf(top_suit)) { 0624 continue; 0625 } 0626 } 0627 } else if (moved_to_empty) { 0628 if (next_talon < 0) 0629 continue; 0630 } else { 0631 // while talons are there, optimisations are evil 0632 // but in end game we have more options 0633 if (next_talon < 0) { 0634 if (index == 0) { 0635 // forbid moves between empty cells once the talons are gone 0636 continue; 0637 } 0638 // there is no plausible reason to split up sequences in end game 0639 if (broken_sequence > 0) { 0640 continue; 0641 } 0642 } 0643 moved_to_empty = true; 0644 } 0645 0646 moves.push_back(Move::regular(from, to, index)); 0647 } 0648 index--; 0649 } 0650 } 0651 0652 if (!one_is_empty && next_talon >= 0) { 0653 moves.push_back(Move::fromTalon(next_talon)); 0654 } 0655 } 0656 0657 void Deck::update(const Deck *other) 0658 { 0659 if (this == other) 0660 return; 0661 memcpy(moves, other->moves, sizeof(Move) * other->moves_index); 0662 moves_index = other->moves_index; 0663 for (int i = 0; i < 10; i++) 0664 play[i] = other->play[i]; 0665 for (int i = 0; i < 5; i++) 0666 talon[i] = other->talon[i]; 0667 off = other->off; 0668 } 0669 0670 std::vector<Move> Deck::getWinMoves() const 0671 { 0672 std::vector<Move> res; 0673 for (int i = 0; i < moves_index; i++) { 0674 res.emplace_back(moves[i]); 0675 } 0676 return res; 0677 } 0678 0679 void Deck::applyMove(const Move &m, Deck &newdeck) const 0680 { 0681 newdeck.update(this); 0682 newdeck.moves[moves_index] = m; 0683 newdeck.moves_index = moves_index + 1; 0684 0685 if (m.talon) { 0686 for (int to = 0; to < 10; to++) { 0687 Card c = newdeck.talon[m.from]->at(to); 0688 c.set_faceup(true); 0689 newdeck.play[to] = newdeck.play[to]->addCard(c); 0690 } 0691 // empty pile 0692 newdeck.talon[m.from] = MemoryManager::self()->createEmptyPile(); 0693 } else if (m.off) { 0694 Card c = newdeck.play[m.from]->at(newdeck.play[m.from]->cardCount() - 13); 0695 newdeck.off = newdeck.off->addCard(c); 0696 newdeck.play[m.from] = newdeck.play[m.from]->remove(m.index); 0697 } else { 0698 newdeck.play[m.to] = newdeck.play[m.to]->copyFrom(newdeck.play[m.from], m.index); 0699 newdeck.play[m.from] = newdeck.play[m.from]->remove(m.index); 0700 } 0701 } 0702 0703 template<class T> 0704 inline void hash_combine(std::size_t &seed, const T &v) 0705 { 0706 std::hash<T> hasher; 0707 seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); 0708 } 0709 0710 uint64_t Deck::id() const 0711 { 0712 std::size_t hash = 0; 0713 for (int i = 0; i < 10; i++) { 0714 hash_combine(hash, play[i]->hash()); 0715 } 0716 for (int i = 0; i < 5; i++) { 0717 hash_combine(hash, talon[i]->hash()); 0718 } 0719 return hash; 0720 } 0721 0722 int Deck::leftTalons() const 0723 { 0724 int talons = 0; 0725 for (int i = 0; i < 5; i++) { 0726 if (!talon[i]->empty()) { 0727 talons++; 0728 } 0729 } 0730 return talons; 0731 } 0732 0733 int Deck::chaos() const 0734 { 0735 int chaos = 0; 0736 for (int i = 0; i < 10; i++) { 0737 chaos += play[i]->chaos(); 0738 } 0739 // per non-empty pile the chaos is at minimum 1 0740 // but if the pile is connected, we substract one 0741 // obvious wins are chaos 0 0742 for (int i = 0; i < 10; i++) { 0743 if (play[i]->cardCount() == 0) { 0744 continue; 0745 } 0746 Card c1 = play[i]->at(0); 0747 0748 if (c1.rank() == 13) { 0749 chaos -= 1; 0750 continue; 0751 } 0752 0753 for (int j = 0; j < 10; j++) { 0754 if (j == i) { 0755 continue; 0756 } 0757 if (play[j]->empty()) { 0758 continue; 0759 } 0760 // we don't need the suit here 0761 if (c1.rank() == play[j]->at(play[j]->cardCount() - 1).rank() - 1) { 0762 chaos--; 0763 break; 0764 } 0765 } 0766 } 0767 int fp = freePlays(); 0768 while (fp > 0 && chaos > 0) { 0769 fp--; 0770 chaos--; 0771 } 0772 return chaos; 0773 } 0774 0775 SolverInterface::ExitStatus Deck::shortestPath() 0776 { 0777 int depth = 1; 0778 bool discarded_siblings = false; 0779 0780 Deck *unvisited = new Deck[REDEALS_NR * LEVEL_CAP]; 0781 int unvisited_count[6] = {0, 0, 0, 0, 0, 0}; 0782 int unvisited_count_total = 0; 0783 unvisited[unvisited_count_total++].update(this); 0784 0785 std::unordered_set<uint64_t> seen; 0786 const int max_new_unvisited = LEVEL_CAP * REDEALS_NR * AVG_OPTIONS; 0787 WeightedDeck *new_unvisited = new WeightedDeck[max_new_unvisited]; 0788 for (int i = 0; i < max_new_unvisited; i++) { 0789 new_unvisited[i].index = i; 0790 } 0791 int new_unvisited_counter = 0; 0792 std::vector<Move> current_moves; 0793 while (!stop_exec) { 0794 for (int i = 0; i < unvisited_count_total; i++) { 0795 unvisited[i].getMoves(current_moves); 0796 for (const Move &m : current_moves) { 0797 size_t deck_index = new_unvisited[new_unvisited_counter].index; 0798 Deck &deck = MemoryManager::self()->deckAt(deck_index); 0799 unvisited[i].applyMove(m, deck); 0800 uint64_t hash = deck.id(); 0801 0802 if (seen.find(hash) != seen.end()) 0803 continue; 0804 0805 new_unvisited[new_unvisited_counter++].update(hash); 0806 seen.insert(hash); 0807 if (max_new_unvisited == new_unvisited_counter) { 0808 std::cerr << "Too many unvisted " << new_unvisited_counter << " AVG too small" << std::endl; 0809 return SolverInterface::MemoryLimitReached; 0810 } 0811 } 0812 } 0813 for (int lt = 0; lt <= 5; lt++) 0814 unvisited_count[lt] = 0; 0815 0816 unvisited_count_total = 0; 0817 0818 if (new_unvisited_counter == 0) 0819 break; 0820 0821 bool printed = false; 0822 std::sort(new_unvisited, new_unvisited + new_unvisited_counter); 0823 for (int i = 0; i < new_unvisited_counter; i++) { 0824 if (!printed) { 0825 #if 0 0826 std::cout << "DEPTH " << depth << " " << new_unvisited_counter << " chaos: " << new_unvisited[i].chaos << " " << int(new_unvisited[i].playable) 0827 << std::endl; 0828 #endif 0829 printed = true; 0830 } 0831 if (new_unvisited[i].in_off == 104) { 0832 Deck &deck = MemoryManager::self()->deckAt(new_unvisited[i].index); 0833 memcpy(moves, deck.moves, sizeof(Move) * deck.moves_index); 0834 moves_index = deck.moves_index; 0835 0836 delete[] unvisited; 0837 delete[] new_unvisited; 0838 return SolverInterface::SolutionExists; 0839 } 0840 int lt = new_unvisited[i].left_talons; 0841 if (unvisited_count[lt] < LEVEL_CAP) { 0842 Deck &deck = MemoryManager::self()->deckAt(new_unvisited[i].index); 0843 unvisited[unvisited_count_total++].update(&deck); 0844 unvisited_count[lt]++; 0845 } else { 0846 discarded_siblings = true; 0847 } 0848 } 0849 0850 new_unvisited_counter = 0; 0851 depth += 1; 0852 } 0853 delete[] unvisited; 0854 delete[] new_unvisited; 0855 if (new_unvisited_counter == 0 && !discarded_siblings) { 0856 return SolverInterface::NoSolutionExists; 0857 } 0858 return SolverInterface::UnableToDetermineSolvability; 0859 } 0860 0861 int Deck::playableCards() const 0862 { 0863 int result = 0; 0864 for (int i = 0; i < 10; i++) 0865 result += play[i]->playableCards(); 0866 return result; 0867 } 0868 0869 int Deck::inOff() const 0870 { 0871 return off->cardCount() * 13; 0872 } 0873 0874 int Deck::freePlays() const 0875 { 0876 int result = 0; 0877 for (int i = 0; i < 10; i++) { 0878 if (play[i]->empty()) { 0879 result++; 0880 } 0881 } 0882 return result; 0883 } 0884 0885 void Deck::makeEmpty() 0886 { 0887 off = MemoryManager::self()->createEmptyPile(); 0888 for (int i = 0; i < 10; i++) 0889 play[i] = MemoryManager::self()->createEmptyPile(); 0890 for (int i = 0; i < 5; i++) 0891 talon[i] = MemoryManager::self()->createEmptyPile(); 0892 moves_index = 0; 0893 } 0894 0895 void Deck::translate(const Spider *dealer) 0896 { 0897 makeEmpty(); 0898 for (int i = 0; i < 5; i++) { 0899 talon[i] = MemoryManager::self()->translatePile(dealer->getRedeal(i), true); 0900 } 0901 for (int i = 0; i < 10; i++) { 0902 play[i] = MemoryManager::self()->translatePile(dealer->getStack(i), false); 0903 } 0904 for (int i = 0; i < 8; i++) { 0905 if (dealer->getLeg(i)->isEmpty()) 0906 continue; 0907 off = off->addCard(Card(dealer->getLeg(i)->topCard())); 0908 } 0909 // std::cout << toString(); 0910 } 0911 0912 SpiderSolver2::SpiderSolver2(Spider *dealer) 0913 { 0914 m_dealer = dealer; 0915 orig = new Deck(); 0916 // singleton 0917 memManager = new MemoryManager(); 0918 } 0919 0920 SpiderSolver2::~SpiderSolver2() 0921 { 0922 delete orig; 0923 delete memManager; 0924 } 0925 0926 SolverInterface::ExitStatus SpiderSolver2::patsolve(int max_positions) 0927 { 0928 m_winMoves.clear(); 0929 if (max_positions > 0) { 0930 // rely on brute force hints - they are good enough 0931 return UnableToDetermineSolvability; 0932 } 0933 stop_exec = false; 0934 0935 ExitStatus ret = orig->shortestPath(); 0936 if (ret == SolutionExists) { 0937 for (auto &move : orig->getWinMoves()) 0938 m_winMoves.push_back(move.to_MOVE()); 0939 } 0940 return ret; 0941 } 0942 0943 void SpiderSolver2::translate_layout() 0944 { 0945 MemoryManager::self()->clean(); 0946 orig->translate(m_dealer); 0947 } 0948 0949 MoveHint SpiderSolver2::translateMove(const MOVE &m) 0950 { 0951 const PatPile *frompile = nullptr; 0952 if (m.from < 10) 0953 frompile = m_dealer->getStack(m.from); 0954 else 0955 return MoveHint(); 0956 0957 KCard *card = frompile->at(m.card_index); 0958 Q_ASSERT(card); 0959 Q_ASSERT(m.to < 10); 0960 if (m.totype == W_Type) 0961 return MoveHint(card, m_dealer->getStack(m.to), m.pri); 0962 0963 // auto move to the off 0964 return MoveHint(); 0965 } 0966 0967 void SpiderSolver2::stopExecution() 0968 { 0969 stop_exec = true; 0970 } 0971 0972 MemoryManager *MemoryManager::m_self = nullptr; 0973 MemoryManager::MemoryManager() 0974 { 0975 Q_ASSERT(!m_self); 0976 m_self = this; 0977 size_t deckstore_size = LEVEL_CAP * REDEALS_NR * AVG_OPTIONS; 0978 // do not call constructors!! 0979 deckstore = (Deck *)malloc(sizeof(Deck) * deckstore_size); 0980 } 0981 0982 void MemoryManager::clean() 0983 { 0984 for (auto &it : pilemap) { 0985 delete it.second; 0986 } 0987 pilemap.clear(); 0988 } 0989 0990 MemoryManager::~MemoryManager() 0991 { 0992 clean(); 0993 free(deckstore); 0994 m_self = nullptr; 0995 } 0996 0997 const Pile *MemoryManager::createEmptyPile() 0998 { 0999 unsigned char newcards[2] = {0, 0}; 1000 return createPile(newcards, 0); 1001 } 1002 1003 const Pile *MemoryManager::translatePile(const KCardPile *pile, bool reverse) 1004 { 1005 unsigned char newcards[MAX_CARDS]; 1006 size_t count = 0; 1007 size_t len = pile->cards().length(); 1008 for (auto &card : pile->cards()) { 1009 size_t index = count; 1010 if (reverse) { 1011 index = len - count - 1; 1012 } 1013 newcards[index] = Card(card).raw_value(); 1014 count++; 1015 } 1016 return createPile(newcards, count); 1017 } 1018 1019 }