-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patheurovision.cpp
More file actions
419 lines (335 loc) · 14.2 KB
/
eurovision.cpp
File metadata and controls
419 lines (335 loc) · 14.2 KB
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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
#include "eurovision.h"
//-------------------------PARTICIPANT IMPLEMENTATION-------------------------
Participant::Participant(const string& state, const string& song, int song_length, const string& singer) :
m_state(state), m_song(song), m_song_length(song_length), m_singer(singer), m_is_registered(false) {
}
// Get functions:
const string& Participant::state() const {
return m_state;
}
const string& Participant::song() const {
return m_song;
}
const string& Participant::singer() const {
return m_singer;
}
int Participant::timeLength() const {
return m_song_length;
}
bool Participant::isRegistered() const {
return m_is_registered;
}
// Set functions:
void Participant::update(const string& name, int length, const string& singer) {
if (m_is_registered) return; // if participant is registered, do nothing
// for each parameter check if it's value is valid
// and update the corresponding participant's field
if (!name.empty()) {
m_song = name;
}
if (length > 0) {
m_song_length = length;
}
if (!singer.empty()) {
m_singer = singer;
}
}
void Participant::updateRegistered(bool registered) {
m_is_registered = registered;
}
//Operators:
ostream& operator<<(ostream& os, const Participant& participant) {
// Output format: [state/song/song length/singer]
os << "[" << participant.state() << "/" << participant.song() << "/";
os << participant.timeLength() << "/" << participant.singer() << "]";
return os;
}
//-------------------------VOTER IMPLEMENTATION-------------------------
Voter::Voter(const string& state, VoterType type) : m_state(state), m_type(type), m_times_of_votes(0) {
}
// Get functions:
int Voter::timesOfVotes() const {
return m_times_of_votes;
}
const string& Voter::state() const {
return m_state;
}
VoterType Voter::voterType() const {
return m_type;
}
// Operators:
Voter& Voter::operator++() {
m_times_of_votes++;
return *this;
}
ostream& operator<<(ostream& os, const Voter& voter) {
// Output format: <state/voterType>
os << "<" << voter.state() << "/";
VoterType type = voter.voterType();
if (type == Regular) {
os << "Regular";
} else if (type == Judge) {
os << "Judge";
}
return os << ">";
}
//-------------------------VOTE IMPLEMENTATION-------------------------
Vote::Vote(Voter& voter, const string& state1,
const string& state2, const string& state3, const string& state4,
const string& state5, const string& state6, const string& state7,
const string& state8, const string& state9, const string& state10) :
m_voter(voter) {
m_states = new string[NUMBER_OF_RANKINGS] {
state1, state2, state3, state4, state5, state6, state7, state8, state9, state10
};
}
Vote::~Vote() {
delete[] m_states;
}
//-------------------------MAINCONTROL IMPLEMENTATION-------------------------
MainControl::MainControl(int max_song_length,
int max_participants,
int max_regular_votes) :
m_max_song_length(max_song_length),
m_max_participants(max_participants),
m_max_regular_votes(max_regular_votes),
m_num_of_participants(0),
m_phase(Registration) {
// Insert dummy node at the beginning
// and end of the participants list
// (with each having a "dummy participant")
Participant* last_dummy = new Participant("","",0,"");
ParticipantNode* last_node = new ParticipantNode(*last_dummy, nullptr);
Participant* first_dummy = new Participant("","",0,"");
ParticipantNode* first_node = new ParticipantNode(*first_dummy, last_node);
m_first = first_node;
m_last = last_node;
}
MainControl::~MainControl() {
// Iterate on the participants list & free all nodes
// that were created in the operator+= function
ParticipantNode* iterator = m_first->next;
while (iterator != m_last) {
iterator->participant.updateRegistered(false);
ParticipantNode* to_delete = iterator;
iterator = iterator->next;
delete to_delete;
}
// Free the "dummy participants" and the dummy nodes
delete &(m_first->participant);
delete m_first;
delete &(m_last->participant);
delete m_last;
}
void MainControl::setPhase(Phase phase) {
if ((m_phase == Registration && phase == Contest) || (m_phase == Contest && phase == Voting)) {
m_phase = phase;
}
}
bool MainControl::participate(const string& state) const {
// Search for the participant node corresponding to the given state
ParticipantNode& prev_node = findPrevNode(state);
ParticipantNode* candidate_node = prev_node.next;
string node_state = candidate_node->participant.state();
// If the node we arrived at is not the dummy at the end
// and the node's state is equal to the given state
// then we know that there is a participant in the list
// with the given state
return (candidate_node != m_last && node_state == state);
}
bool MainControl::legalParticipant(const Participant& participant) const {
// Check that participant's state, singer and song names are all valid
if (participant.state().empty() || participant.singer().empty() || participant.song().empty()) {
return false;
}
// Check that the participant's song length is valid
int time_length = participant.timeLength();
return (1 <= time_length && time_length <= m_max_song_length);
}
MainControl& MainControl::operator+=(Participant& participant) {
if (m_phase != Registration) return *this; // Registration phase is over
if (m_max_participants <= m_num_of_participants) return *this; // Reached max participants
if (participate(participant.state())) return *this; // Already participating
if (!legalParticipant(participant)) return *this; // Invalid Participant
// Search for the place in the list the new participant should be added
ParticipantNode& prev_node = findPrevNode(participant.state());
// Put the new participant node in the list (prev_node -> new_node -> prev_node.next)
ParticipantNode* new_node = new ParticipantNode(participant, prev_node.next);
prev_node.next = new_node;
participant.updateRegistered(true); // set the participant as registered
m_num_of_participants++; // increment num of participants in MainControl
return *this;
}
MainControl& MainControl::operator-=(Participant& participant) {
// Check registration phase is not over and participant is participating
if (m_phase != Registration || !participant.isRegistered()) return *this;
// Remove the node in the list where the participant is in and free
ParticipantNode& prev_node = findPrevNode(participant.state());
ParticipantNode* to_delete = prev_node.next;
prev_node.next = prev_node.next->next;
delete to_delete;
participant.updateRegistered(false); // set the participant as not registered
m_num_of_participants--; // reduce count of participants in MainControl
return *this;
}
MainControl& MainControl::operator+=(const Vote& vote) {
// Check it's Voting phase and that the Voter's state is participating
if (m_phase != Voting) return *this;
if (!participate(vote.m_voter.state())) return *this;
// Add points according to the voter type
if (vote.m_voter.voterType() == Regular) {
if (vote.m_voter.timesOfVotes() >= m_max_regular_votes) return *this; // reached regular voting limit
addPointsIfLegal(vote.m_voter, vote.m_states[0], 1); // add 1 point to voted state
} else if (vote.m_voter.voterType() == Judge) {
if (vote.m_voter.timesOfVotes() > 0) return *this; // reached judge voting limit
for (int i=0; i < NUMBER_OF_RANKINGS; i++) {
addPointsIfLegal(vote.m_voter, vote.m_states[i], getRanking(i)); // add points according to ranking
}
}
return *this;
}
string MainControl::operator()(int place, VoterType type) const {
// check place is valid
if (place < 1 || m_num_of_participants < place) return "";
// get the state that finished at n-th place
// the comparison is done based on the vote type
Iterator winner = get<Iterator,VoteCompare>(begin(), end(), place, VoteCompare(type));
return (*winner).state(); // if this is the end dummy -> empty string is returned
}
ostream& operator<<(ostream& os, const MainControl& eurovision) {
MainControl::ParticipantNode* iterator = eurovision.m_first->next; // iterator for the participants list
MainControl:: ParticipantNode* end = eurovision.m_last;
os << "{" << endl << MainControl::getPhaseText(eurovision.m_phase) << endl;
if (eurovision.m_phase == Registration) {
// Print participants
while (iterator != end) {
os << iterator->participant << endl;
iterator = iterator->next;
}
} else if (eurovision.m_phase == Voting) {
// Print the amount of votes (of each type) each participating state has
while (iterator != end) {
os << iterator->participant.state() << " : ";
os << "Regular(" << iterator->m_regular_votes << ") ";
os << "Judge(" << iterator->m_judge_votes << ")" << endl;
iterator = iterator->next;
}
}
os << "}" << endl;
return os;
}
//-------------------------MAINCONTROL ITERATOR FUNCTIONS-------------------------
MainControl::Iterator::Iterator() : current(nullptr) {} // initialize to point on nullptr
bool MainControl::Iterator::operator<(const Iterator& other) const {
if (current->next == nullptr) return false; // current == end()
if (other.current->next == nullptr) return true; // other.current == end()
// use the fact that the container is sorted by the states name
Participant& p1 = current->participant;
Participant& p2 = other.current->participant;
return p1.state() < p2.state();
}
MainControl::Iterator& MainControl::Iterator::operator++() {
// check if reached the last node
// if reached last node, stay there
if (current->next != nullptr) { // current != end()
current = current->next;
}
return *this;
}
bool MainControl::Iterator::operator==(const Iterator& other) const {
return current == other.current;
}
Participant& MainControl::Iterator::operator*() const {
assert(current->next != nullptr); // can't dereference the end dummy
return (this->current->participant);
}
MainControl::Iterator MainControl::begin() const {
MainControl::Iterator iter;
iter.current = m_first->next;
return iter;
}
MainControl::Iterator MainControl::end() const {
MainControl::Iterator iter;
iter.current = m_last;
return iter;
}
//-------------------------VOTECOMPARE FUNCTIONS----------------------------------
MainControl::VoteCompare::VoteCompare(VoterType v_type) : type(v_type) {}
bool MainControl::VoteCompare::operator()(MainControl::Iterator iter1, MainControl::Iterator iter2) {
// get num of votes the pointed data
ParticipantNode* node1 = iter1.current;
int regular_votes_1 = node1->m_regular_votes;
int judge_votes_1 = node1->m_judge_votes;
ParticipantNode* node2 = iter2.current;
int regular_votes_2 = node2->m_regular_votes;
int judge_votes_2 = node2->m_judge_votes;
int votes_1 = 0, votes_2 = 0;
switch (type) {
case Regular: // compare only Regular votes
votes_1 = regular_votes_1;
votes_2 = regular_votes_2;
break;
case Judge: // compare only Judge votes
votes_1 = judge_votes_1;
votes_2 = judge_votes_2;
break;
default: //case All: compare sum of Regular & Judge votes
votes_1 = regular_votes_1 + judge_votes_1;
votes_2 = regular_votes_2 + judge_votes_2;
break;
}
if (votes_1 == votes_2) {
// if no. of points is equal, the state with the bigger name
// goes first (true = Participant 1 has bigger state)
return node1->participant.state() > node2->participant.state();
}
// true = Participant 1 has more votes than Participant 2
return votes_1 > votes_2;
}
//-------------------------MAINCONTROL INTERNAL FUNCTIONS-------------------------
MainControl::ParticipantNode& MainControl::findPrevNode(const string& state) const {
ParticipantNode* iterator = m_first;
string next_state = iterator->next->participant.state();
// Iterate on the participants list while the given state is lexicographically
// smaller than the next node's state
while (iterator->next != m_last && state > next_state) {
iterator = iterator->next;
next_state = iterator->next->participant.state();
}
// After the loop, the iterator should be pointing to the
// node which goes *before* a node that holds participant
// with the given state (whether that node is in the list or not)
return *iterator;
}
void MainControl::addPointsIfLegal(Voter& voter, const string& voted_state, int num_of_points) const {
// Check that the vote is valid:
// - Voted state is participating in this Eurovision
// - Voter state and voted state are different
if (!participate(voted_state) || voter.state() == voted_state) return;
// Get the participant node of the voted state
// in order to update it's points
ParticipantNode& prev_node = findPrevNode(voted_state);
ParticipantNode* participant = prev_node.next;
// Add points to the voted state accordingly
if (voter.voterType() == Regular) {
participant->m_regular_votes += num_of_points;
} else if (voter.voterType() == Judge) {
participant->m_judge_votes += num_of_points;
}
++(voter); // increments the number of times the voter has voted
}
string MainControl::getPhaseText (Phase phase) {
if (phase == Registration) return "Registration";
if (phase == Voting) return "Voting";
return "Contest";
}
Ranking MainControl::getRanking(int rank) {
// points table for judges points
static const Ranking ranking[NUMBER_OF_RANKINGS] = {
FIRST_PLACE, SECOND_PLACE, THIRD_PLACE, FOURTH_PLACE,
FIFTH_PLACE, SIXTH_PLACE, SEVENTH_PLACE, EIGHT_PLACE,
NINTH_PLACE, TENTH_PLACE
};
return ranking[rank];
}
// -----------------------------------------------------------