parent
a3956e3bad
commit
e7a414bcc4
@ -0,0 +1,44 @@
|
||||
# Generated by Django 3.1 on 2020-09-10 16:21
|
||||
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0012_alter_user_first_name_max_length'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='LameUser',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
||||
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
|
||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'user',
|
||||
'verbose_name_plural': 'users',
|
||||
'abstract': False,
|
||||
},
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
]
|
@ -0,0 +1,38 @@
|
||||
# Generated by Django 3.1 on 2020-09-10 16:21
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('games', '0006_auto_20200908_1344'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Card',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('suit', models.IntegerField(choices=[(0, 'None'), (1, 'Diamond'), (2, 'Spade'), (3, 'Heart'), (4, 'Club')])),
|
||||
('value', models.IntegerField(choices=[(0, 'Joker'), (1, 'One'), (2, 'Two'), (3, 'Three'), (4, 'Four'), (5, 'Five'), (6, 'Six'), (7, 'Seven'), (8, 'Eight'), (9, 'Nine'), (10, 'Ten'), (11, 'Jack'), (12, 'Queen'), (13, 'King')])),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MinesweeperCell',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('shown', models.BooleanField(default=False)),
|
||||
('bomb', models.BooleanField()),
|
||||
('bombs_next', models.SmallIntegerField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MinesweeperBoard',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('cells', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='board', to='games.minesweepercell')),
|
||||
],
|
||||
),
|
||||
]
|
@ -0,0 +1,25 @@
|
||||
# Generated by Django 3.1 on 2020-09-10 16:35
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('games', '0007_card_minesweeperboard_minesweepercell'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='Card',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='minesweeperboard',
|
||||
name='user',
|
||||
field=models.OneToOneField(default=None, on_delete=django.db.models.deletion.CASCADE, to='common.lameuser'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -0,0 +1,27 @@
|
||||
# Generated by Django 3.1 on 2020-09-10 16:37
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('games', '0008_auto_20200910_1635'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='activeuser',
|
||||
name='user',
|
||||
field=models.OneToOneField(default=0, on_delete=django.db.models.deletion.CASCADE, to='common.lameuser'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='minesweeperboard',
|
||||
name='user',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='games.activeuser'),
|
||||
),
|
||||
]
|
@ -0,0 +1,21 @@
|
||||
# Generated by Django 3.1 on 2020-09-10 18:09
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('games', '0009_auto_20200910_1637'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='minesweeperboard',
|
||||
name='user',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
@ -0,0 +1,24 @@
|
||||
# Generated by Django 3.1 on 2020-09-10 18:12
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('games', '0010_auto_20200910_1809'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='minesweeperboard',
|
||||
name='cells',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='minesweepercell',
|
||||
name='board',
|
||||
field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to='games.minesweeperboard'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -0,0 +1,31 @@
|
||||
# Generated by Django 3.1 on 2020-09-10 18:23
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('games', '0011_auto_20200910_1812'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='minesweepercell',
|
||||
name='x',
|
||||
field=models.SmallIntegerField(default=0),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='minesweepercell',
|
||||
name='y',
|
||||
field=models.SmallIntegerField(default=0),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='minesweepercell',
|
||||
name='board',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cells', to='games.minesweeperboard'),
|
||||
),
|
||||
]
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.1 on 2020-09-10 21:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('games', '0012_auto_20200910_1823'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='minesweepercell',
|
||||
name='flagged',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.1 on 2020-09-10 22:14
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('games', '0013_minesweepercell_flagged'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='minesweepercell',
|
||||
name='game_over',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
@ -0,0 +1,22 @@
|
||||
# Generated by Django 3.1 on 2020-09-10 22:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('games', '0014_minesweepercell_game_over'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='minesweepercell',
|
||||
name='game_over',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='minesweeperboard',
|
||||
name='game_over',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.1 on 2020-09-10 23:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('games', '0015_auto_20200910_2215'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='minesweeperboard',
|
||||
name='win_lose',
|
||||
field=models.BooleanField(default=None),
|
||||
),
|
||||
]
|
@ -0,0 +1,27 @@
|
||||
# Generated by Django 3.1 on 2020-09-10 23:46
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('games', '0016_minesweeperboard_win_lose'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='minesweeperboard',
|
||||
name='game_over',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='minesweeperboard',
|
||||
name='win_lose',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='minesweeperboard',
|
||||
name='status',
|
||||
field=models.IntegerField(choices=[(0, 'In Progress'), (1, 'Lost'), (2, 'Won')], default=0),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -0,0 +1,22 @@
|
||||
# Generated by Django 3.1 on 2020-09-11 00:25
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('games', '0017_auto_20200910_2346'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='activeuser',
|
||||
name='username',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='minesweeperboard',
|
||||
name='status',
|
||||
field=models.IntegerField(choices=[(0, 'In Progress'), (1, 'Lost'), (2, 'Won')], default=0),
|
||||
),
|
||||
]
|
@ -0,0 +1,181 @@
|
||||
const WSProtocol = window.location.protocol === "https:" ? "wss" : "ws";
|
||||
const MSSocket = new WebSocket(
|
||||
WSProtocol
|
||||
+ '://'
|
||||
+ window.location.host
|
||||
+ '/minesweeper/'
|
||||
+ "1"
|
||||
);
|
||||
|
||||
const RED_FLAG_UNICODE = '🚩';
|
||||
|
||||
/* TODO: remove global variable */
|
||||
let LATEST_BUTTON;
|
||||
|
||||
const help_menu = () => {
|
||||
write_message("h: Help menu")
|
||||
write_message("n: New game");
|
||||
write_message("f: Toggle flag on current tile");
|
||||
write_message("Space/Enter: Expose current tile");
|
||||
write_message("w/a/s/d: up/left/down/right");
|
||||
};
|
||||
|
||||
const send_click = (e, event_type) => {
|
||||
LATEST_BUTTON = e.target;
|
||||
bid = Number(LATEST_BUTTON.id.split('-').pop());
|
||||
if (event_type === 'flagged')
|
||||
{
|
||||
// if is number, meaning: already shown
|
||||
if (/[0-9]/.test(LATEST_BUTTON.innerHTML))
|
||||
{
|
||||
write_message("<i>You cannot flag a square that is already showing</i>");
|
||||
} else {
|
||||
if (LATEST_BUTTON.innerHTML !== RED_FLAG_UNICODE)
|
||||
{
|
||||
LATEST_BUTTON.innerHTML = RED_FLAG_UNICODE;
|
||||
write_message("Flagged " + (bid % 10) + "," + Math.floor(bid / 10));
|
||||
} else {
|
||||
LATEST_BUTTON.innerHTML = '';
|
||||
write_message("Unflagged " + (bid % 10) + "," + Math.floor(bid / 10));
|
||||
}
|
||||
}
|
||||
} else if (event_type === 'clicked') {
|
||||
// if flagged
|
||||
if (LATEST_BUTTON.innerHTML === RED_FLAG_UNICODE)
|
||||
{
|
||||
write_message('<i>You cannot click on a flagged element!</i>')
|
||||
return;
|
||||
}
|
||||
if (/[0-9]/.test(LATEST_BUTTON.innerHTML))
|
||||
{
|
||||
write_message('<i>You have already exposed this peice!</i>');
|
||||
return;
|
||||
}
|
||||
}
|
||||
MSSocket.send(JSON.stringify(
|
||||
{
|
||||
'type': event_type,
|
||||
'button_id': bid
|
||||
}
|
||||
));
|
||||
};
|
||||
|
||||
const moving_key_handler = (e) => {
|
||||
/* if no last button: do not change */
|
||||
if (!LATEST_BUTTON)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
old_id = Number(LATEST_BUTTON.id.split('-').pop());
|
||||
console.log("OID: " + old_id);
|
||||
/* TODO: remove hardcoded values */
|
||||
if (e.key === 'w') {
|
||||
new_id = old_id -= 10;
|
||||
} else if (e.key === 'a') {
|
||||
new_id = old_id -= 1;
|
||||
} else if (e.key === 's') {
|
||||
new_id = old_id += 10;
|
||||
} else if (e.key === 'd') {
|
||||
new_id = old_id += 1;
|
||||
}
|
||||
|
||||
/* if new ID over/under limit: stop */
|
||||
/* TODO: remove hardcoded values */
|
||||
if (new_id > 99 || new_id < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("NID: " + new_id);
|
||||
/* focus on new button ID */
|
||||
document.getElementById("mscell-" + new_id).focus();
|
||||
};
|
||||
|
||||
const gen_new_board = () => {
|
||||
write_message("<i>Generating new board...</i>")
|
||||
MSSocket.send(JSON.stringify({
|
||||
'type': 'generate'
|
||||
}));
|
||||
};
|
||||
|
||||
const global_key_handler = (e) => {
|
||||
console.log(e.key);
|
||||
if (e.key === 'n') {
|
||||
gen_new_board();
|
||||
} else if (e.key === 'h') {
|
||||
help_menu();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
MSSocket.onmessage = (e) => {
|
||||
console.log(e.data);
|
||||
console.log(JSON.parse(e.data));
|
||||
data = JSON.parse(e.data);
|
||||
|
||||
if (data.type === 'display') {
|
||||
let shown_tiles = 0;
|
||||
for (cell of data.partial_board)
|
||||
{
|
||||
i = (cell.y*10) + cell.x;
|
||||
btn = document.getElementById("mscell-" + i);
|
||||
btn.innerHTML = cell.bombs_next;
|
||||
shown_tiles++;
|
||||
}
|
||||
if (data.flagged)
|
||||
{
|
||||
for (cell of data.flagged)
|
||||
{
|
||||
i = (cell.y*10) + cell.x;
|
||||
btn = document.getElementById("mscell-" + i);
|
||||
btn.innerHTML = '🚩';
|
||||
}
|
||||
}
|
||||
write_message("You have exposed " + shown_tiles + " tiles");
|
||||
} else if (data.type === 'display_full') {
|
||||
for (cell of data.full_board)
|
||||
{
|
||||
i = (cell.y*10) + cell.x;
|
||||
btn = document.getElementById("mscell-" + i);
|
||||
if (cell.bomb) {
|
||||
btn.innerHTML = '💣';
|
||||
} else {
|
||||
btn.innerHTML = cell.bombs_next;
|
||||
}
|
||||
}
|
||||
} else if (data.type === 'game_over') {
|
||||
MSSocket.send(JSON.stringify({
|
||||
'type': 'full_board'
|
||||
}));
|
||||
} else if (data.type === 'new_board') {
|
||||
for (btn of document.getElementsByClassName("cell"))
|
||||
{
|
||||
btn.innerHTML = '';
|
||||
}
|
||||
} else if (data.type === 'message') {
|
||||
write_message(data.message);
|
||||
}
|
||||
};
|
||||
|
||||
/* TODO: remove hardcoded "clicked"/"flagged" Low-Priority */
|
||||
for (cell of document.getElementsByClassName("cell")) {
|
||||
cell.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'f') {
|
||||
send_click(e, 'flagged');
|
||||
}
|
||||
});
|
||||
cell.addEventListener('keydown', moving_key_handler);
|
||||
cell.addEventListener('click', (e) => {
|
||||
send_click(e, 'clicked');
|
||||
});
|
||||
cell.addEventListener('contextmenu', (e) => {
|
||||
send_click(e, 'flagged');
|
||||
});
|
||||
cell.addEventListener('focus', (e) => {
|
||||
LATEST_BUTTON = e.target;
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('keypress', global_key_handler);
|
||||
help_menu();
|
@ -0,0 +1,10 @@
|
||||
<div id="minesweeper-board">
|
||||
{% for row in board %}
|
||||
<div class="row">
|
||||
{% for cell in row %}
|
||||
<label hidden for="mscell-{{ cell.id }}">{{ cell.x }},{{ cell.y }}</label>
|
||||
<button class="cell" id="mscell-{{ cell.id }}"></button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
@ -0,0 +1,8 @@
|
||||
{% extends 'common/master.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block body %}
|
||||
{% include 'games/minesweeper/board.html' %}
|
||||
{% include 'games/gamelog.html' %}
|
||||
<script src="{% static 'games/js/minesweeper.js' %}"></script>
|
||||
{% endblock %}
|
Loading…
Reference in new issue