Skip to content

Conversation

@yushiuan9499
Copy link
Collaborator

Description

TOJ will be used as an online judge for exams.
To avoiding cheating, we introduced a new "Random Set" contest mode in this PR.

In this contest mode, each problem number corresponds to a problem set.
And if a problem set contains more than 2 problems, any adjacent contestants will always see different problems. Otherwise, the assignment is randomized.

Implementation

  • IP-Based Assignment: Because the exams are held in the classroom, the adjacent computers always have adjacent IP addresses. This allows us to assign problems based on the request's IP address.
  • Admin Controls: The admin interface allows for:
    • Defining the IP range for the contest.
    • Adding, removing, and reordering problem sets.
  • Database Changes:
    • One new table contest_ip_joints is added to record the problems assign to the ip.
    • Two columns start_ip and end_ip are added to table contests to record the contests' ip range.

Note

This PR only completes the admin-side features, user-side features like scoreboard will be done later.

Without pro_sets, it is hard to do many operations.
Such as generate pro_set list for manage UI.
The value of param has been checked at handler. We don't need to check
twice.
Because the RequestHandler instance of each request is independent, the
contest is always consistent with the database. This means db is the
only thing must be updated.
Because ip is default to 0.0.0.0, the ip_range of contest can't be None.
This reverts commit 78d86f1.

Because of the redis cache, the contest is also need to be update to set
redis cache.
Both add_pro_set & update_ip need to append a random problem to the
ip_pro_list, but only add_pro_set need to update pro_list & do some
check, so I let this logic be a single function.
If there are no ip in the db, there will be no item in ip_pro_list and ,
as a result, get 0 pro_set count.

Use sql to get count of distinct order is a more stable way.
Comment on lines +415 to +440
async def add_random_pro(self, contest: Contest, pro_set: list[int]):
'''
Append a random problem in pro_set to each ip's problem list
'''
pro_size = len(pro_set)
if pro_size == 1:
for pro_list in contest.ip_pro_list.values():
pro_list.append(pro_set[0])
elif pro_size == 2:
for pro_list in contest.ip_pro_list.values():
pro_list.append(pro_set[random.randint(0, 1)])
else:
idx = 0
for pro_list in contest.ip_pro_list.values():
idx = (idx + random.randint(1,pro_size-1)) % pro_size
pro_list.append(pro_set[idx])

async with self.db.acquire() as con:
# Insert por_id into contest_ip_joints
await con.executemany(
'''
INSERT INTO contest_ip_joints ("contest_id", "ip", "pro_id")
VALUES ($1, $2, $3)
''',
[(contest.contest_id, str(ip), pro_list[-1]) for ip, pro_list in contest.ip_pro_list.items()]
)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the main algorithm to assign problems to contestants.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant