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.

Copy link
Collaborator

@tobiichi3227 tobiichi3227 Jan 18, 2026

Choose a reason for hiding this comment

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

So how should I get all problems from a single account?
By passing account ip address?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I didn't implement that feature.

Should I create a page that contains the table showing the pro_ids assigned to each IP address?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Not UI.
We need an interface internally to handle this, because most of the functions only have access to the Account or acct_id.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ok, I understood.
Yes, caller should passing account's IP address to get all problems.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

But, I haven't implement get_problems function.
I plan to implement that function when I am completing the user-side features.

Copy link
Collaborator

Choose a reason for hiding this comment

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

OK.

Current swapRow direct change innerHTML, this will cause event listener
failed.
index.reload() will not refresh full page.
@tobiichi3227 tobiichi3227 changed the base branch from v2.0 to tnfsh January 21, 2026 15:59
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.

2 participants