forked from udacity/fullstack-nanodegree-vm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tournament.py
227 lines (183 loc) · 6.09 KB
/
tournament.py
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
#!/usr/bin/env python
#
# tournament.py -- implementation of a Swiss-system tournament
#
import psycopg2
import pprint
from MatchMaker import MatchMaker
pp = pprint.PrettyPrinter(indent=4)
def connect():
"""Connect to the PostgreSQL database. Returns a database connection
and a cursor in a tuple."""
connection = psycopg2.connect("dbname=tournament user=vagrant")
cursor = connection.cursor()
return (connection, cursor)
def deleteMatches():
"""Remove all the match records from the database."""
conn, cur = connect()
cur.execute("TRUNCATE TABLE matches;")
conn.commit()
conn.close()
return
def deletePlayers():
"""Remove all the player records from the database."""
conn, cur = connect()
cur.execute("TRUNCATE TABLE players CASCADE;")
conn.commit()
conn.close()
return
def countPlayers():
"""Returns the number of players currently registered."""
conn, cur = connect()
cur.execute("select count(*) as number from players;")
(res, ) = cur.fetchone()
conn.close()
if res is None or res == '0':
res = 0
return res
def registerPlayer(name):
"""Adds a player to the tournament database.
The database assigns a unique serial id number for the player. (This
should be handled by your SQL database schema, not in your Python code.)
Args:
name: the player's full name (need not be unique).
"""
conn, cur = connect()
cur.execute("INSERT INTO players (name) VALUES (%s);", (name,))
conn.commit()
conn.close()
return
def playerStandings():
"""Returns a list of the players and their win records, sorted by wins.
The first entry in the list should be the player in first place, or a
player tied for first place if there is currently a tie.
Returns:
A list of tuples, each of which contains (id, name, wins, matches):
id: the player's unique id (assigned by the database)
name: the player's full name (as registered)
wins: the number of matches the player has won
matches: the number of matches the player has played
"""
conn, cur = connect()
sql = '''
select * from standings;
'''
cur.execute(sql)
results = cur.fetchall()
conn.close()
standing = [(
int(row[1]), # id
str(row[0]), # name
int(row[3]), # wins
int(row[4]) # matches
) for row in results]
return standing
def oponentHistory(id):
""" Returns the list of oponents ids a player as already played in this
tournament
Arg: player id
"""
conn, cur = connect()
sql = '''
(select loser as oponents from matches where winner= %s )
union all
(select winner as oponents from matches where loser= %s )
'''
cur.execute(sql, (id, id))
rows = cur.fetchall()
conn.close()
result = [row[0] for row in rows]
return result
def havePlayersMetBefore(player1, player2):
"""Simply checks if two players have met before
This is the only way I could prevent rematches.
I tried to do it in the database but failed.
"""
conn, cur = connect()
sql = '''
select * from history
where (player = %s and oponent = %s);
'''
cur.execute(sql, (player1, player2))
rows = cur.fetchall()
conn.close()
games = [int(row[0]) for row in rows]
return bool(len(games))
def reportMatch(winner, loser):
"""Records the outcome of a single match between two players.
Arguments:
winner: the id number of the player who won
loser: the id number of the player who lost
"""
# Prevent rematches
if(havePlayersMetBefore(winner, loser)):
print '* Ignoring rematch!*', winner, loser
return
conn, cur = connect()
sql = '''
INSERT INTO matches (winner, loser)
VALUES (%s, %s);
'''
cur.execute(sql, (winner, loser))
conn.commit()
conn.close()
def get_tounament_player_dict():
''' Read the payer names and id'''
conn, cur = connect()
sql = '''
select * from players;
'''
cur.execute(sql)
rows = cur.fetchall()
conn.close()
result = {int(row[0]): str(row[1]) for row in rows}
return result
def get_posible_games():
''' Returns a datastucture where all the players are grouped by number of wins
and have a list of all their possible oponents for the next game.
'''
conn, cur = connect()
sql = '''
select * from posible_games;
'''
cur.execute(sql)
rows = cur.fetchall()
conn.close()
result = {}
for row in rows:
(id, oponent_id, wins) = row
result.setdefault(
int(wins), {}).setdefault(int(id), []).append(int(oponent_id))
return result
def swissPairings():
"""Returns a list of pairs of players for the next round of a match.
Assuming that there are an even number of players registered, each player
appears exactly once in the pairings. Each player is paired with another
player with an equal or nearly-equal win record, that is, a player adjacent
to him or her in the standings.
Returns:
A list of tuples, each of which contains (id1, name1, id2, name2)
id1: the first player's unique id
name1: the first player's name
id2: the second player's unique id
name2: the second player's name
"""
next_round = []
posible_games = get_posible_games()
player_dict = get_tounament_player_dict()
# Iterate through the groups of players with same number of wins
for group in posible_games:
# Create a matchmaker and feed it all the players for this group
match_maker = MatchMaker()
for player in posible_games[group]:
match_maker.add_player(player, posible_games[group][player])
# Keep making matches until there are no more players
while match_maker.is_more():
(player1_id, player2_id) = match_maker.make_match()
next_round.append((
player1_id,
player_dict[player1_id],
player2_id,
player_dict[player1_id]
))
return next_round