Skip to content

Commit 45e9a22

Browse files
authored
Merge branch 'master' into bigchanges
2 parents 90bda5c + 724764c commit 45e9a22

12 files changed

Lines changed: 734 additions & 6 deletions

File tree

.github/workflows/ci.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,31 @@ jobs:
6666
with:
6767
parallel: true
6868

69+
docs:
70+
runs-on: ubuntu-latest
71+
72+
steps:
73+
- name: Set up repo
74+
uses: actions/checkout@v2
75+
76+
- name: Set up Python ${{ matrix.python-version }}
77+
uses: actions/setup-python@v5
78+
with:
79+
python-version: 3.14
80+
81+
- name: Install the latest version of uv
82+
uses: astral-sh/setup-uv@v7
83+
84+
- name: Install dependencies
85+
run: uv sync
86+
87+
- name: Build Documentation
88+
run: uv run zensical build --strict
89+
6990
finish_success:
7091
needs:
7192
- test
93+
- docs
7294

7395
runs-on: ubuntu-latest
7496

.github/workflows/docs.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Documentation
2+
on:
3+
push:
4+
branches:
5+
- master
6+
- main
7+
permissions:
8+
contents: read
9+
pages: write
10+
id-token: write
11+
jobs:
12+
deploy:
13+
environment:
14+
name: github-pages
15+
url: ${{ steps.deployment.outputs.page_url }}
16+
runs-on: ubuntu-latest
17+
18+
steps:
19+
- uses: actions/configure-pages@v5
20+
- uses: actions/checkout@v5
21+
- name: Install the latest version of uv
22+
uses: astral-sh/setup-uv@v7
23+
- uses: actions/setup-python@v5
24+
with:
25+
python-version: 3.x
26+
- name: Install dependencies
27+
run: uv sync --frozen
28+
- name: Build Documentation
29+
run: uv run zensical build --clean
30+
- uses: actions/upload-pages-artifact@v4
31+
with:
32+
path: site
33+
- uses: actions/deploy-pages@v4
34+
id: deployment

docs/administrative/tournaments.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Creating a Tournament
2+
3+
This document details:
4+
5+
- An explanation of the different tournament pairing algorithms
6+
- How to create a tournament
7+
8+
## Pairing Algorithms
9+
Othello implements several different algorithms for pairing students
10+
in a tournaments:
11+
12+
- Round Robin: each student plays every other student as both `x` and `o`.
13+
By default, this happens twice (i.e. each student will play as `x` and `o` twice), although
14+
it is customizable in the management form.
15+
- Random: randomly pairs each player with one other player.
16+
- Danish: pairs the 1st and second 2nd place, 3rd and 4th place, and so on.
17+
Both players will get a chance to play as both `x` and `o`.
18+
- Swiss: Danish pairing, but tries to avoid rematches whenever possible.
19+
20+
## Tournament Creation
21+
In order to create a tournament, you must either be a teacher, staff, or superuser.
22+
The creation form is located at Tournaments->Tournament Management.
23+
Simply fill out the form, making sure the start date is in the future, and the tournament
24+
will start at the scheduled time.

docs/images/logo.svg

Lines changed: 8 additions & 0 deletions
Loading

docs/index.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Othello Overview
2+
3+
The official documentation site for https://othello.tjhsst.edu/
4+
5+
## What is Othello?
6+
If you are new to Othello (AKA Reversi), please check out the
7+
[official rules](https://www.worldothello.org/about/about-othello/othello-rules/official-rules/english)[^1].
8+
9+
[^1]: We play a variant of Othello that cares not only about having more
10+
tokens then the opponent, but also about _how many_ more tokens the winner
11+
has compared to the opponent.
12+
13+
## What is Othello Tourney?
14+
Othello Tourney is a TJ CSL service that allows students to compete to build
15+
the best Othello AI. In a match, one player will play as "x", and the other as "o",
16+
corresponding to black and white respectively.
17+
On their turn, a player's python code has a limited amount of time to find the
18+
optimal move for the current board. Whoever wins the most tokens at the end of the
19+
game is considered the winner.
20+
21+
The amount of time given varies based on the match, and other factors. Good Citizen timing
22+
(AKA Time Hoarding) means that if a player takes up less time than the maximum allotted on that
23+
term, the excess time is added on to their time limit on the next turn. In this scheme,
24+
it is thus useful to spend less time on the earlier moves so that more time can be spent
25+
on the later moves.
26+
27+
Othello Tourney also hosts the annual TJ Othello Competition, where the best scoring bots
28+
of all TJ students and alumni compete to find the best bot.
29+
30+
For help creating a submission, see the [submissions page](submissions.md)

docs/submissions.md

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
# Submissions
2+
3+
## File Specification
4+
A valid Othello submission consists of a Python 3.x file, with a class called
5+
`Strategy` and a method called `best_strategy`. A minimal example is shown below:
6+
```py
7+
def choose_move(board: str, player: str, still_running, time_limit: float):
8+
# Implement this method
9+
return 0
10+
11+
12+
class Strategy:
13+
# Uncomment the below flags as needed
14+
# logging = True
15+
# uses_10x10_board = True
16+
# uses_10x10_moves = True
17+
18+
def best_strategy(self, board: str, player: str, best_move, still_running, time_limit: float):
19+
move = choose_move(board, player, still_running, time_limit)
20+
best_move.value = move
21+
22+
```
23+
24+
## Method Specification
25+
The `best_strategy` method must accept at least 4 parameters:
26+
27+
- `board`: a string of length 64, containing the characters `#!python {'.', 'x', 'o'}`
28+
The `.` represents an empty space, `x` represents black, and `o` represents white.
29+
- `player`: a character in `#!python {'x', 'o'}` that represents who your script is playing
30+
as.
31+
- `best_move`: A `multiprocessing.Value` object of type `int`
32+
- `still_running`: A `multiprocessing.Value` object of type `int`
33+
34+
Once you have determined your move, set `best_move.value` to the index of the board at which
35+
to place your token.
36+
37+
!!! note
38+
39+
You do *NOT* need to import `multiprocessing` to use the `multiprocessing.Value` objects.
40+
41+
## Time Management
42+
To know when you run out of time, you can use `still_running.value`, which starts as 1 but changes to 0 when
43+
you near the time limit. Alternatively, `best_strategy` can accept an optional 5th argument, `time_limit`.
44+
If included in your method header, it will be set to an integer representing the amount of time (in seconds)
45+
that your script has to determine the best move.
46+
47+
!!! tip "Time Hoarding"
48+
49+
If your game supports "Good Citizen" timing, exiting the `best_strategy` method before the time limit will give you extra time on the next turn.
50+
51+
52+
!!! warning
53+
54+
All user code will be run in a daemonized `multiprocessing.Process`. This means your script cannot spawn any Processes of its own.
55+
If your code does spawn any `multiprocessing.Process` instances, it will error out.
56+
57+
## Logging
58+
In addition to the specifications listed above, your `Strategy` class may contain a `logging` variable.
59+
This variable, if used, must be an attribute of the `Strategy` class.
60+
Therefore it should be defined within `Strategy`'s constructor (as `self.logging`) or inside the class but outside a class method (just `logging`).
61+
For instance, `logging` cannot be defined in the `best_strategy` method of your `Strategy` class or in a helper method defined outside of `Strategy`.
62+
63+
If set to `True`, you will see the output of any `#!python print` statements on either side of the board when playing your script.
64+
Output for scripts that are playing as black will appear on the left-hand side of the Othello board while the output for scripts that are
65+
playing as white will appear on the right-hand side of the Othello board.
66+
67+
If you omit the `logging` variable in your `Strategy` class, it will be assumed that you do not wish to output any `#!python print`
68+
statements.
69+
70+
!!! info
71+
72+
If you set `logging = True` in your `Strategy` class, you will only be able to view script logs if you are
73+
currently logged in and own the running script.
74+
75+
76+
## Customizing Board Representation
77+
If you would like to be provided with a 10x10 board instead of the default 8x8 board, you can add a `uses_10x10_board` variable to your strategy class.
78+
This will surround the 8x8 board with an additional layer of `'?'` characters, as shown below.
79+
Similar to the `logging` variable, `uses_10x10_board` must be an attribute of the `Strategy` class and will default to `False` if omitted.
80+
81+
You can also use the `uses_10x10_moves` variable to signify that your submitted `best_move.value` refers to indices in this 10x10 board representation.
82+
If not used, the server will assume that your `best_move.value` refers to indices in the default 8x8 board, irrespective of whether or not `uses_10x10_board` is enabled.
83+
84+
```
85+
? ? ? ? ? ? ? ? ? ?
86+
. . . . . . . . ? . . . . . . . . ?
87+
. . . . . . . . ? . . . . . . . . ?
88+
. . . . . . . . ? . . . . . . . . ?
89+
. . . o x . . . ? . . . o x . . . ?
90+
. . . x o . . . => ? . . . x o . . . ?
91+
. . . . . . . . ? . . . . . . . . ?
92+
. . . . . . . . ? . . . . . . . . ?
93+
. . . . . . . . ? . . . . . . . . ?
94+
? ? ? ? ? ? ? ? ? ?
95+
96+
```
97+
## Upload Errors
98+
When uploading code to the server you may encounter one of the following errors.
99+
100+
- File has invalid syntax
101+
* The Othello server was unable to validate your code because there was a syntax error somewhere in the file.
102+
Go back through your code and revise any syntax issues then upload again.
103+
- Cannot find attribute `Strategy.best_strategy` in file
104+
* Your code does not follow the specifications listed above.
105+
Reread the specifications and revise your code before submitting again.
106+
- Attribute `Strategy.best_strategy` has an invalid amount of parameters
107+
* Your code does not follow the specifications listed above.
108+
Specifically regarding the amount of parameters `Strategy.best_strategy` should take.
109+
Reread the specifications and revise your code before submitting again.
110+
- Code takes too long to validate!
111+
* Your file has some code that is outside a function or outside an `#!python if __name__ == '__main__'` block.
112+
Any code that is outside such block must terminate after 1s.
113+
Go back through your file and ensure that code that runs on import terminates quickly.
114+
115+
If you encounter another error message that you cannot interpret, email othello@tjhsst.edu
116+
a screenshot of the error message as well as your code.
117+
118+
## Playing Games and Errors
119+
120+
To play a game, go to the Games->Play page and select two players.
121+
To watch a game, go to the Games->Watch page and select one of the listed games.
122+
If there are no games currently being played, no games will be listed.
123+
124+
125+
When playing games, if your script behaves incorrectly, the server will interpret this as an `UserError`.
126+
When the server encounters an `UserError`, your script will automatically forfeit the game and your opponent will be awarded the win.
127+
The `UserError` will be reported in either log area, regardless if the playing script set the `logging` variable.
128+
The following are the possible `UserError` codes and their meanings:
129+
130+
- An error code of `#!python -1` is a `NO_MOVE_ERROR`, meaning your script did not submit any move before the time limit
131+
- An error code of `#!python -2` is a `READ_INVALID` error, meaning your script submitted something to
132+
the `best_move` variable but it was not an integer in the range 0-63, inclusive.
133+
- An error code of `#!python -3` is an `INVALID_MOVE` error, meaning your script submitted an integer
134+
in the range 0-63, inclusive, but it was not a valid move given the current board state.
135+
136+
If any other error code is outputted, namely an error code within `#!python {-4,-5,-6, -7, -8}`, this means the server has
137+
encountered an error while running your game. In this case, the game will be marked as a tie between both players, but
138+
you should email othello@tjhsst.edu a screenshot of the game along with your code
139+
so we can investigate and fix this error.
140+
141+
Finally, when running a non-tournament game, the server continuously checks if the browser which initiated the game is
142+
still watching the game. If not, the game will be terminated and the game will end in a tie. Meaning, if you start
143+
a game but then close the window with the game still running, the game will be terminated and end in a tie.
144+
145+
146+
## Multiple Submissions
147+
You may submit code any amount of times, and the Othello server will record and store all your submissions. If you wish, you may retrieve any
148+
previously submitted script from the upload page. However, the Othello Server will only run your most recent submission.
149+
150+
All your code submissions will persist on the Othello server, and you can retrieve previous submissions through the Games->Upload page.
151+
The Othello server will always use your most recent code submission when running your AI against other players.
152+
(If you were to submit twice, your second submission would be used to play against other users)
153+
154+
!!! note "Labeling Scripts"
155+
156+
When uploading a script, you will be given an option to attach a "name" or "label" to that script.
157+
Adding a "name" to your script will make it easier to identify previous submissions if you wish to retrieve them later.
158+
If omitted, your script's "name" will default to the time it was submitted.
159+
160+
161+
## Running Multiple Files
162+
163+
As of now the Othello server will only run the most recently uploaded script.
164+
You should try to put all your code in a single file and upload that file.
165+
Any files created by your code will persist between runs and games.
166+
If you do in fact need other files for your code to work, and it is infeasible to include it in one file,
167+
contact othello@tjhsst.edu or your AI teacher about your situation.
168+
169+
!!! warning
170+
171+
When your code is uploaded, it is stored under a different, randomly-generated filename. Submitting two Python files and expecting to be
172+
able to import the other file by name is infeasible.
173+
174+
175+
## Replaying Past Games
176+
177+
178+
After either playing or watching a game, you may download a text-formatted version of the game using the "Download Game" button below the game board.
179+
You may choose to download the game in a format that is pretty-printed and easy to interpret visually or in a format that is more easily parsed programmatically.
180+
The exact specifications for both formats, and a preview, are shown when downloading a game.
181+
182+
In addition to downloading games, you can upload this file to the Games->Replay page and more closely interact with the downloaded game file.
183+
After uploading the game file, you will be able to step forward and step back through all the turns played in that game.
184+
185+
!!! warning
186+
187+
Logging data is not saved in game files and will not be displayed during replays
188+
189+
190+
## Other Info
191+
192+
If your code fails to return a move, returns an invalid move, or errors for whatever reason, it will be treated as a forfeit.
193+
As such, make sure you code works on your computer before using this website to test against other AIs.
194+
Just as a reminder, if your code is caught cheating during the tournament in any way, you **will** be given an integrity violation, so just don't do it.
195+

othello/asgi.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import os
22

33
import django
4-
from channels.routing import get_default_application
54

65
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "othello.settings")
76

87
django.setup()
9-
application = get_default_application()
8+
9+
from .routing import application # noqa: E402,F401

othello/sandboxing/sandbox.profile

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ notv
1212
novideo
1313
no3d
1414

15-
shell none
16-
1715
# Resource limits
1816
# 1G memory, 10M files, 500 open file descriptors, 1000 processes
1917
rlimit-as 1000000000

othello/settings/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
}
7979
]
8080

81-
ASGI_APPLICATION = "othello.routing.application"
81+
ASGI_APPLICATION = "othello.asgi.application"
8282
WSGI_APPLICATION = "othello.wsgi.application"
8383

8484
DATABASES = {

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ dependencies = [
2626
[dependency-groups]
2727
dev = [
2828
"coverage>=7.13.1",
29+
"zensical>=0.0.20",
2930
]
3031

3132
[tool.uv]

0 commit comments

Comments
 (0)