-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcards.cpp
221 lines (190 loc) · 7.47 KB
/
cards.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/*
doko is a C++ doppelkopf program with an integrated UCT player.
Copyright (c) 2011-2016 Silvan Sievers
For questions, please write to: [email protected]
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 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
The idea of implementing cards using a single long long value stems from
the Skat player by Sebastian Kupferschmid, released under the GNU GENERAL
PUBLIC LICENSE Version 2, described in "Entwicklung eines Double-Dummy Skat
Solvers mit einer Anwendung für verdeckte Skatspiele" (Sebastian Kupferschmid,
University of Freiburg, 2003).
*/
#include "cards.h"
#include "game_type.h"
#include <algorithm>
#include <cassert>
#include <iostream>
#include <map>
using namespace std;
Card::Card() : value(0) {
}
Card::Card(int number) : value(1ULL << number) {
}
const int Card::card_to_index[53]= {
-1, 0, 1, 17, 2, 47, 18, 14, 3, 34,
-1, 6, 19, 24, 15, 12, 4, 10, 35, 37,
-1, 31, 7, 39, 20, 42, 25, -1, 16, 46,
13, 33, 5, 23, 11, 9, 36, 30, 38, 41,
-1, 45, 32, 22, 8, 29, 40, 44, 21, 28,
43, 27, 26
};
const char *const Card::card_names[48] = {
"H9", "H9", "HK", "HK", "HA", "HA",
"S9", "S9", "SK", "SK", "S1", "S1", "SA", "SA",
"C9", "C9", "CK", "CK", "C1", "C1", "CA", "CA",
"D9", "D9", "DK", "DK", "D1", "D1", "DA", "DA",
"DJ", "DJ", "HJ", "HJ", "SJ", "SJ", "CJ", "CJ",
"DQ", "DQ", "HQ", "HQ", "SQ", "SQ", "CQ", "CQ",
"H1", "H1"
};
const int Card::card_values[48] = {
0, 0, 4, 4, 11, 11,
0, 0, 4, 4, 10, 10, 11, 11,
0, 0, 4, 4, 10, 10, 11, 11,
0, 0, 4, 4, 10, 10, 11, 11,
2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3,
10, 10
};
ostream &operator<<(ostream &out, Card card) {
out << "[" << card.get_name() << "]";
return out;
}
// Compares two given Cards according to their rank, depending on the GameType
struct CardComparator {
const GameType *game_type;
CardComparator(const GameType *game_type_) : game_type(game_type_) {}
bool operator()(Card lhs, Card rhs) const {
bool lhs_is_trump = game_type->get_trump_suit().contains_card(lhs);
bool rhs_is_trump = game_type->get_trump_suit().contains_card(rhs);
if (lhs_is_trump != rhs_is_trump) { // one card is trump, the other is not
return lhs_is_trump;
} else {
if (lhs_is_trump) { // both cards are trump
return game_type->get_trump_rank(lhs) <= game_type->get_trump_rank(rhs);
} else { // both cards are non trump
const vector<Cards> &non_trump_suits = game_type->get_non_trump_suits();
for (size_t i = non_trump_suits.size() - 1; i >= 0; --i) {
bool lhs_is_of_current_suit = non_trump_suits[i].contains_card(lhs);
bool rhs_is_of_current_suit = non_trump_suits[i].contains_card(rhs);
if (lhs_is_of_current_suit && rhs_is_of_current_suit) // both cards are of the current non trump suit
return game_type->get_non_trump_rank(lhs) <= game_type->get_non_trump_rank(rhs);
if (lhs_is_of_current_suit) // only lhs is of the current non trump suit, thus ordering it before rhs
return true;
if (rhs_is_of_current_suit) // only rhs is of the current non trump suit, thus ordering lhs behind rhs
return false;
if (i == 0) { // avoid letting get i negative (which would cause an overflow, because its of type size_t)
assert(false); // in the last iteration, one of the three tests above MUST succeed! thus we never reach this
break;
}
}
}
assert(false);
return true;
}
}
};
Cards::Cards() : value(0) {
}
Cards::Cards(const Card &card) : value(card.value) {
}
void Cards::print(ostream &out, const GameType *game_type) const {
vector<Card> single_cards;
get_single_cards(single_cards);
sort(single_cards.begin(), single_cards.end(), CardComparator(game_type));
out << "[";
for (size_t i = 0; i < single_cards.size(); ++i) {
out << single_cards[i].get_name();
if (i != single_cards.size() - 1)
out << ", ";
}
out << "]";
}
char Cards::bits_in_16bits[65536] = {""};
void Cards::setup_bit_count() {
for (int i = 0; i < 65536; ++i) {
// from wikipedia: Hamming_weight
int x = (i & m1 ) + ((i >> 1) & m1 ); //put count of each 2 bits into those 2 bits
x = (x & m2 ) + ((x >> 2) & m2 ); //put count of each 4 bits into those 4 bits
x = (x & m4 ) + ((x >> 4) & m4 ); //put count of each 8 bits into those 8 bits
x = (x & m8 ) + ((x >> 8) & m8 ); //put count of each 16 bits into those 16 bits
bits_in_16bits[i] = x;
}
}
void Cards::remove_card(const Card &card) {
assert(contains_card(card));
value ^= card.value;
}
void Cards::remove_cards(const Cards &cards) {
vector<Card> single_cards;
cards.get_single_cards(single_cards);
for (size_t i = 0; i < single_cards.size(); ++i) {
Card card = single_cards[i];
if (contains_card(single_cards[i])) {
remove_card(card);
}
}
// TODO: why does this not work?
/*value ^= cards.value;
vector<Card> single_cards;
cards.get_single_cards(single_cards);
for (size_t i = 0; i < single_cards.size(); ++i)
assert(!contains_card(single_cards[i]));*/
}
Cards Cards::get_intersection(const Cards &cards) const {
Cards cards_intersection;
cards_intersection.value = (value & cards.value);
return cards_intersection;
}
int Cards::size() const {
int result = bits_in_16bits[value & 0xffffu]
+ bits_in_16bits[(value >> 16) & 0xffffu]
+ bits_in_16bits[(value >> 32) & 0xffffu];
return result;
}
void Cards::get_single_cards(vector<Card> &cards) const {
cards.reserve(size());
for (int i = 0; i < 48; ++i) {
Card card(i);
if (contains_card(card))
cards.push_back(card);
}
}
void Cards::show(const GameType *game_type) const {
if (game_type == 0) // cannot use a default parameter game_type = ®ular because game_type uses Card/Cards classes
game_type = ®ular;
print(cout, game_type);
cout << endl;
}
ostream &operator<<(ostream &out, Cards cards) {
cards.print(out, ®ular);
return out;
}
static map<string, pair<Card, Card> > name_to_card;
bool is_valid_card_name(const string &name) {
if (name_to_card.empty()) {
for (size_t i = 0; i < 48; i += 2) {
assert(string(Card::get_card_names()[i]) == string(Card::get_card_names()[i + 1]));
name_to_card[Card::get_card_names()[i]] = make_pair(Card(i), Card(i + 1));
}
}
return name_to_card.count(name);
}
pair<Card, Card> get_cards_for_name(const string &name) {
assert(is_valid_card_name(name));
return name_to_card[name];
}
int next_player(int player) {
return (player + 1) % 4;
}