Merge branch 'matt-server'
This commit is contained in:
commit
164cf6a73a
7
.editorconfig
Normal file
7
.editorconfig
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[*.cr]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/docs/
|
||||||
|
/lib/
|
||||||
|
/bin/
|
||||||
|
/.shards/
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "external/GoUI"]
|
||||||
|
path = external/GoUI
|
||||||
|
url = https://dev.danilafe.com/CS-290/GoUI.git
|
BIN
.sass-cache/2da5b7a9ed68f2839637c88aab8550dd9bb593bb/main.scssc
Normal file
BIN
.sass-cache/2da5b7a9ed68f2839637c88aab8550dd9bb593bb/main.scssc
Normal file
Binary file not shown.
1
.travis.yml
Normal file
1
.travis.yml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
language: crystal
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018 Danila Fedorin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
4
Makefile
Normal file
4
Makefile
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
generate-elm :
|
||||||
|
cd external/GoUI && elm make Go.elm --output ../../public/js/Go.js
|
||||||
|
generate-css :
|
||||||
|
scss public/scss/main.scss > public/css/main.css
|
41
README.md
41
README.md
|
@ -1 +1,40 @@
|
||||||
# Final Project for Group 4
|
# Go
|
||||||
|
|
||||||
|
This is an implementation of a game of GO in web application form.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
TODO: Write installation instructions for Windows
|
||||||
|
___
|
||||||
|
TODO: Write installation instructions for Mac
|
||||||
|
___
|
||||||
|
Arch Linux:
|
||||||
|
1. Install dependencies: `pacman -S crystal shards ruby-sass`
|
||||||
|
2. Install Shards: `shards install`
|
||||||
|
3. TODO: Add elm compilation
|
||||||
|
4. Compile SASS Style: `make generate-css`
|
||||||
|
5. Compile Crystal app: `crystal build --release src/Go.cr`
|
||||||
|
6. Run with `./Go`
|
||||||
|
___
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
While crystal app is running, go to localhost with port of 3000 in the browser.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
TODO: Write development instructions here
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
1. Fork it ( https://github.com/[your-github-name]/Go/fork )
|
||||||
|
2. Create your feature branch (git checkout -b my-new-feature)
|
||||||
|
3. Commit your changes (git commit -am 'Add some feature')
|
||||||
|
4. Push to the branch (git push origin my-new-feature)
|
||||||
|
5. Create a new Pull Request
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
- [[your-github-name]](https://github.com/[your-github-name]) Danila Fedorin - creator, maintainer
|
||||||
|
|
||||||
|
- [sessionm21](https://github.com/sessionm21) Matthew Sessions - hax0r, maintainer
|
||||||
|
|
1
external/GoUI
vendored
Submodule
1
external/GoUI
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit b68222dab7a766ede897ba9bdc68f262e2194e3d
|
BIN
game_saves.db
Normal file
BIN
game_saves.db
Normal file
Binary file not shown.
115
public/css/main.css
Normal file
115
public/css/main.css
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-family: "Indie Flower", serif;
|
||||||
|
text-align: center; }
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 5em;
|
||||||
|
margin: 0px; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Raleway", sans-serif;
|
||||||
|
margin: 0px;
|
||||||
|
background-color: #f4f4f4; }
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
max-width: 750px;
|
||||||
|
margin: auto; }
|
||||||
|
|
||||||
|
.board {
|
||||||
|
background-color: tomato;
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 500px;
|
||||||
|
margin: auto;
|
||||||
|
border-radius: 10px; }
|
||||||
|
|
||||||
|
.black-player .board-cell:hover .overlay {
|
||||||
|
background-color: black; }
|
||||||
|
|
||||||
|
.white-player .board-cell:hover .overlay {
|
||||||
|
background-color: white; }
|
||||||
|
|
||||||
|
.board-cell {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
padding: 5.5555555556%; }
|
||||||
|
.board-cell .overlay {
|
||||||
|
position: absolute;
|
||||||
|
box-sizing: border-box;
|
||||||
|
top: 10%;
|
||||||
|
left: 10%;
|
||||||
|
width: 80%;
|
||||||
|
height: 80%;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: background-color .25s; }
|
||||||
|
.board-cell.small {
|
||||||
|
padding: 5.5555555556%; }
|
||||||
|
.board-cell.medium {
|
||||||
|
padding: 3.8461538462%; }
|
||||||
|
.board-cell.large {
|
||||||
|
padding: 2.6315789474%; }
|
||||||
|
|
||||||
|
.black-cell .overlay {
|
||||||
|
background-color: black; }
|
||||||
|
|
||||||
|
.white-cell .overlay {
|
||||||
|
background-color: white; }
|
||||||
|
|
||||||
|
.split-wrapper {
|
||||||
|
display: flex;
|
||||||
|
width: 100%; }
|
||||||
|
@media screen and (max-width: 640px) {
|
||||||
|
.split-wrapper {
|
||||||
|
flex-direction: column; } }
|
||||||
|
|
||||||
|
.split-item {
|
||||||
|
flex-grow: 1;
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
.split-wrapper form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px; }
|
||||||
|
.split-wrapper form input {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 5px;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 300px; }
|
||||||
|
.split-wrapper form input[type="radio"] {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0px;
|
||||||
|
height: 0px; }
|
||||||
|
.split-wrapper form input[type="radio"]:checked ~ label {
|
||||||
|
color: tomato;
|
||||||
|
transition: color .25s; }
|
||||||
|
.split-wrapper form input[type="submit"] {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: tomato;
|
||||||
|
color: white; }
|
||||||
|
.split-wrapper form input[type="submit"]:focus, .split-wrapper form input[type="submit"]:hover {
|
||||||
|
background-color: inherit;
|
||||||
|
color: tomato;
|
||||||
|
transition: background-color .25s, color .25s; }
|
||||||
|
.split-wrapper form input[type="text"] {
|
||||||
|
background-color: inherit;
|
||||||
|
border-bottom: solid tomato;
|
||||||
|
border-width: 2px;
|
||||||
|
height: 3em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block; }
|
||||||
|
.split-wrapper form input[type="text"]:focus {
|
||||||
|
border-width: 3px;
|
||||||
|
transition: background-color .25s, border-width .25s; }
|
||||||
|
.split-wrapper form .radio-parent {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 300px; }
|
||||||
|
.split-wrapper form .radio-wrapper {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center; }
|
9744
public/js/Go.js
Normal file
9744
public/js/Go.js
Normal file
File diff suppressed because it is too large
Load Diff
169
public/scss/main.scss
Normal file
169
public/scss/main.scss
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
$background-grey: #f4f4f4;
|
||||||
|
|
||||||
|
@mixin board-cell($size) {
|
||||||
|
padding: 100%/$size/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-family: "Indie Flower", serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 5em;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Raleway", sans-serif;
|
||||||
|
margin: 0px;
|
||||||
|
background-color: $background-grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
max-width: 750px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board {
|
||||||
|
background-color: tomato;
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 500px;
|
||||||
|
margin: auto;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.black-player {
|
||||||
|
.board-cell:hover .overlay {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.white-player {
|
||||||
|
.board-cell:hover .overlay {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-cell {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
@include board-cell(9);
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
position: absolute;
|
||||||
|
box-sizing: border-box;
|
||||||
|
top: 10%;
|
||||||
|
left: 10%;
|
||||||
|
width: 80%;
|
||||||
|
height: 80%;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: background-color .25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
@include board-cell(9);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.medium {
|
||||||
|
@include board-cell(13);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.large {
|
||||||
|
@include board-cell(19);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.black-cell {
|
||||||
|
.overlay {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.white-cell {
|
||||||
|
.overlay {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-wrapper {
|
||||||
|
display: flex;
|
||||||
|
@media screen and (max-width: 640px) {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-item {
|
||||||
|
flex-grow: 1;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-wrapper form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 5px;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"] {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0px;
|
||||||
|
height: 0px;
|
||||||
|
|
||||||
|
&:checked ~ label {
|
||||||
|
color: tomato;
|
||||||
|
transition: color .25s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="submit"] {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: tomato;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&:focus, &:hover {
|
||||||
|
background-color: inherit;
|
||||||
|
color: tomato;
|
||||||
|
transition: background-color .25s, color .25s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
background-color: inherit;
|
||||||
|
border-bottom: solid tomato;
|
||||||
|
border-width: 2px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-width: 3px;
|
||||||
|
transition: background-color .25s, border-width .25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
height: 3em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-parent {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-wrapper {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
59
public/svg/black.svg
Normal file
59
public/svg/black.svg
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
height="100"
|
||||||
|
width="100"
|
||||||
|
version="1.1"
|
||||||
|
id="svg4"
|
||||||
|
sodipodi:docname="black.svg"
|
||||||
|
inkscape:version="0.92.2 2405546, 2018-03-11">
|
||||||
|
<metadata
|
||||||
|
id="metadata10">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs8" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1159"
|
||||||
|
inkscape:window-height="719"
|
||||||
|
id="namedview6"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.36"
|
||||||
|
inkscape:cx="50"
|
||||||
|
inkscape:cy="50"
|
||||||
|
inkscape:window-x="307"
|
||||||
|
inkscape:window-y="38"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="svg4" />
|
||||||
|
<circle
|
||||||
|
cx="50"
|
||||||
|
cy="50"
|
||||||
|
r="40"
|
||||||
|
stroke="black"
|
||||||
|
stroke-width="3"
|
||||||
|
fill="red"
|
||||||
|
id="circle2"
|
||||||
|
style="fill:#000000" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
58
public/svg/circle.svg
Normal file
58
public/svg/circle.svg
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
height="100"
|
||||||
|
width="100"
|
||||||
|
version="1.1"
|
||||||
|
id="svg4"
|
||||||
|
sodipodi:docname="circle.svg"
|
||||||
|
inkscape:version="0.92.2 2405546, 2018-03-11">
|
||||||
|
<metadata
|
||||||
|
id="metadata10">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs8" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1159"
|
||||||
|
inkscape:window-height="719"
|
||||||
|
id="namedview6"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.36"
|
||||||
|
inkscape:cx="50"
|
||||||
|
inkscape:cy="50"
|
||||||
|
inkscape:window-x="307"
|
||||||
|
inkscape:window-y="38"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="svg4" />
|
||||||
|
<circle
|
||||||
|
cx="50"
|
||||||
|
cy="50"
|
||||||
|
r="40"
|
||||||
|
stroke="black"
|
||||||
|
stroke-width="3"
|
||||||
|
fill="red"
|
||||||
|
id="circle2"
|
||||||
|
style="fill:#000000" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
64
public/svg/cross.svg
Normal file
64
public/svg/cross.svg
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
height="100"
|
||||||
|
width="100"
|
||||||
|
version="1.1"
|
||||||
|
id="svg14"
|
||||||
|
sodipodi:docname="crossB.svg"
|
||||||
|
inkscape:version="0.92.2 2405546, 2018-03-11">
|
||||||
|
<metadata
|
||||||
|
id="metadata20">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs18" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1028"
|
||||||
|
id="namedview16"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="8.27"
|
||||||
|
inkscape:cx="50"
|
||||||
|
inkscape:cy="50"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg14" />
|
||||||
|
<line
|
||||||
|
x1="50"
|
||||||
|
y1="0"
|
||||||
|
x2="50"
|
||||||
|
y2="100"
|
||||||
|
style="stroke:rgb(0,0,0);stroke-width:2"
|
||||||
|
id="line10" />
|
||||||
|
<line
|
||||||
|
x1="0"
|
||||||
|
y1="50"
|
||||||
|
x2="100"
|
||||||
|
y2="50"
|
||||||
|
style="stroke:rgb(0,0,0);stroke-width:2"
|
||||||
|
id="line12" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
59
public/svg/white.svg
Normal file
59
public/svg/white.svg
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
height="100"
|
||||||
|
width="100"
|
||||||
|
version="1.1"
|
||||||
|
id="svg4"
|
||||||
|
sodipodi:docname="white.svg"
|
||||||
|
inkscape:version="0.92.2 2405546, 2018-03-11">
|
||||||
|
<metadata
|
||||||
|
id="metadata10">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs8" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1159"
|
||||||
|
inkscape:window-height="719"
|
||||||
|
id="namedview6"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.36"
|
||||||
|
inkscape:cx="50"
|
||||||
|
inkscape:cy="50"
|
||||||
|
inkscape:window-x="307"
|
||||||
|
inkscape:window-y="38"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="svg4" />
|
||||||
|
<circle
|
||||||
|
cx="50"
|
||||||
|
cy="50"
|
||||||
|
r="40"
|
||||||
|
stroke="black"
|
||||||
|
stroke-width="3"
|
||||||
|
fill="red"
|
||||||
|
id="circle2"
|
||||||
|
style="fill:#ffffff" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
22
shard.lock
Normal file
22
shard.lock
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
version: 1.0
|
||||||
|
shards:
|
||||||
|
db:
|
||||||
|
github: crystal-lang/crystal-db
|
||||||
|
version: 0.5.0
|
||||||
|
|
||||||
|
kemal:
|
||||||
|
github: kemalcr/kemal
|
||||||
|
version: 0.22.0
|
||||||
|
|
||||||
|
kilt:
|
||||||
|
github: jeromegn/kilt
|
||||||
|
version: 0.4.0
|
||||||
|
|
||||||
|
radix:
|
||||||
|
github: luislavena/radix
|
||||||
|
version: 0.3.8
|
||||||
|
|
||||||
|
sqlite3:
|
||||||
|
github: crystal-lang/crystal-sqlite3
|
||||||
|
version: 0.9.0
|
||||||
|
|
21
shard.yml
Normal file
21
shard.yml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
name: Go
|
||||||
|
version: 0.1.0
|
||||||
|
|
||||||
|
authors:
|
||||||
|
- Danila Fedorin <danila.fedorin@gmail.com>
|
||||||
|
|
||||||
|
targets:
|
||||||
|
Go:
|
||||||
|
main: src/Go.cr
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
kemal:
|
||||||
|
github: kemalcr/kemal
|
||||||
|
db:
|
||||||
|
github: crystal-lang/crystal-db
|
||||||
|
sqlite3:
|
||||||
|
github: crystal-lang/crystal-sqlite3
|
||||||
|
|
||||||
|
crystal: 0.24.2
|
||||||
|
|
||||||
|
license: MIT
|
9
spec/Go_spec.cr
Normal file
9
spec/Go_spec.cr
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
require "./spec_helper"
|
||||||
|
|
||||||
|
describe Go do
|
||||||
|
# TODO: Write tests
|
||||||
|
|
||||||
|
it "works" do
|
||||||
|
false.should eq(true)
|
||||||
|
end
|
||||||
|
end
|
2
spec/spec_helper.cr
Normal file
2
spec/spec_helper.cr
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
require "spec"
|
||||||
|
require "../src/Go"
|
199
src/Go.cr
Normal file
199
src/Go.cr
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
require "./Go/*"
|
||||||
|
require "kemal"
|
||||||
|
require "json"
|
||||||
|
|
||||||
|
require "db"
|
||||||
|
require "sqlite3"
|
||||||
|
|
||||||
|
URL = "localhost"
|
||||||
|
PORT = "3000"
|
||||||
|
GAME_CACHE = {} of String => Go::Game
|
||||||
|
GAME_SAVE = "./game_saves.db"
|
||||||
|
|
||||||
|
def save_game(db, gameid, game)
|
||||||
|
# Function: save_game
|
||||||
|
# Parameters: db(String)[Unused] gameid(String) game(Go::Game)
|
||||||
|
turn, size, white_pass, black_pass, board = game.encode
|
||||||
|
DB.open "sqlite3:./#{GAME_SAVE}" do |db|
|
||||||
|
# Create table if one does not exist, gameid is UNIQUE => No duplicates
|
||||||
|
db.exec "create table if not exists game_saves (gameid string, turn integer, size integer, white_pass string, black_pass string, board string, UNIQUE(gameid) )"
|
||||||
|
# If duplicate => replace values, else => make new row for gameid
|
||||||
|
db.exec "insert or replace into game_saves values (?, ?, ?, ?, ?, ?)", gameid, turn.value, size, white_pass, black_pass, board
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def query_game(db, gameid) : Go::Game?
|
||||||
|
# Function: query_game
|
||||||
|
# Parameters: db(String)[Unused] gameid(String)
|
||||||
|
turn = 0
|
||||||
|
size = Go::Size::Small
|
||||||
|
white_pass = ""
|
||||||
|
black_pass = ""
|
||||||
|
board = ""
|
||||||
|
begin
|
||||||
|
DB.open "sqlite3:./#{GAME_SAVE}" do |db|
|
||||||
|
# Query whole row where the gameid is found
|
||||||
|
db.query "SELECT * FROM game_saves WHERE gameid = ?", gameid do |rs|
|
||||||
|
rs.each do
|
||||||
|
id = rs.read(String) # Reduntant
|
||||||
|
turn = rs.read(Int32)
|
||||||
|
size = rs.read(Int32)
|
||||||
|
white_pass = rs.read(String)
|
||||||
|
black_pass = rs.read(String)
|
||||||
|
board = rs.read(String)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# New Go::Game object
|
||||||
|
game = Go::Game.new()
|
||||||
|
game.size = Go::Size.from_value(size)
|
||||||
|
game.white_pass = white_pass
|
||||||
|
game.black_pass = black_pass
|
||||||
|
game.turn = Go::Color.from_value(turn)
|
||||||
|
# Parses game board string
|
||||||
|
counter = 0
|
||||||
|
# For each character in the board String
|
||||||
|
board.each_char do |char|
|
||||||
|
x = counter % 9
|
||||||
|
y = counter / 9
|
||||||
|
coord = {x.to_i8, y.to_i8}
|
||||||
|
if(char == 'B')
|
||||||
|
game.board[coord] = Go::Color::Black
|
||||||
|
elsif(char == 'W')
|
||||||
|
game.board[coord] = Go::Color::White
|
||||||
|
end
|
||||||
|
counter += 1
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
# Catch bad query
|
||||||
|
puts "DB query Failed"
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
# Finished Go::Game object to return
|
||||||
|
return game
|
||||||
|
end
|
||||||
|
|
||||||
|
def lookup_game(db, cache, id) : Go::Game?
|
||||||
|
# Function: lookup_game
|
||||||
|
# Parameters: db(String)[Unused] cache({(String), (Go::Game)}) id(String)
|
||||||
|
if game = cache[id]?
|
||||||
|
return game
|
||||||
|
else
|
||||||
|
loaded_game = query_game(db, id)
|
||||||
|
# Need to convert id to string for some reason
|
||||||
|
cache[id.to_s] = loaded_game if loaded_game
|
||||||
|
return loaded_game
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_game(db, cache, game, id)
|
||||||
|
# Function: create_game
|
||||||
|
# Parameters: db(String)[Unused] cache({(String), (Go::Game)}) game(Go::Game) id(String)
|
||||||
|
cache[id] = game
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_message(id, game, socket, message)
|
||||||
|
# Function: handle_message
|
||||||
|
# Parameters: id(String) game(Go::Game) socket() message()
|
||||||
|
|
||||||
|
split_command = message.split(" ")
|
||||||
|
command = split_command[0]
|
||||||
|
if command == "place"
|
||||||
|
x = split_command[1].to_i8
|
||||||
|
y = split_command[2].to_i8
|
||||||
|
color = split_command[3] == "Black" ? Go::Color::Black : Go::Color::White
|
||||||
|
|
||||||
|
game.update(x, y, color)
|
||||||
|
game.sockets.each { |socket| socket.send game.to_json }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/" do |env|
|
||||||
|
render "src/Go/views/index.ecr", "src/Go/views/base.ecr"
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/save" do |env|
|
||||||
|
GAME_CACHE.each do |game_hash|
|
||||||
|
gameid, game = game_hash
|
||||||
|
save_game("none", gameid, game)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
post "/game" do |env|
|
||||||
|
game_id = env.params.body["id"]?
|
||||||
|
game_password = env.params.body["password"]?
|
||||||
|
if game_id == nil || game_password == nil
|
||||||
|
render_404
|
||||||
|
elsif game = lookup_game(nil, GAME_CACHE, game_id)
|
||||||
|
id = game_id
|
||||||
|
size = game.size.value
|
||||||
|
black = nil
|
||||||
|
|
||||||
|
if game_password == game.black_pass
|
||||||
|
black = true
|
||||||
|
elsif game_password == game.white_pass
|
||||||
|
black = false
|
||||||
|
end
|
||||||
|
|
||||||
|
black.try { |black| render "src/Go/views/game.ecr", "src/Go/views/base.ecr"} || render_404
|
||||||
|
else
|
||||||
|
render_404
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
post "/create" do |env|
|
||||||
|
game_id = env.params.body["id"]?
|
||||||
|
user_password = env.params.body["your-password"]?
|
||||||
|
other_password = env.params.body["their-password"]?
|
||||||
|
color = env.params.body["color"]?
|
||||||
|
|
||||||
|
color_e = nil
|
||||||
|
if color == "black"
|
||||||
|
color_e = Go::Color::Black
|
||||||
|
elsif color == "white"
|
||||||
|
color_e = Go::Color::White
|
||||||
|
end
|
||||||
|
|
||||||
|
if game_id == nil || user_password == nil || other_password == nil || color == nil || color_e == nil
|
||||||
|
render_404
|
||||||
|
elsif game = lookup_game(nil, GAME_CACHE, game_id)
|
||||||
|
render_404
|
||||||
|
else
|
||||||
|
color_e = color_e.as(Go::Color)
|
||||||
|
user_password = user_password.as(String)
|
||||||
|
other_password = other_password.as(String)
|
||||||
|
if color_e == Go::Color::Black
|
||||||
|
white_pass, black_pass = other_password, user_password
|
||||||
|
else
|
||||||
|
white_pass, black_pass = user_password, other_password
|
||||||
|
end
|
||||||
|
game = Go::Game.new(Go::Size::Small, black_pass, white_pass)
|
||||||
|
create_game(nil, GAME_CACHE, game, game_id.as(String))
|
||||||
|
|
||||||
|
id = game_id
|
||||||
|
size = game.size.value
|
||||||
|
black = color_e == Go::Color::Black
|
||||||
|
render "src/Go/views/game.ecr", "src/Go/views/base.ecr"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ws "/game/:id" do |socket, env|
|
||||||
|
game_id = env.params.url["id"]
|
||||||
|
if game = lookup_game(nil, GAME_CACHE, game_id)
|
||||||
|
socket.send game.to_json
|
||||||
|
game.sockets << socket
|
||||||
|
|
||||||
|
socket.on_message do |message|
|
||||||
|
game.try { |game| handle_message(game_id, game, socket, message) }
|
||||||
|
end
|
||||||
|
|
||||||
|
socket.on_close do
|
||||||
|
game.try { |game| game.sockets.delete socket }
|
||||||
|
end
|
||||||
|
else
|
||||||
|
render_404
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Kemal.run
|
147
src/Go/Game.cr
Normal file
147
src/Go/Game.cr
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
module Go
|
||||||
|
enum Color
|
||||||
|
Black
|
||||||
|
White
|
||||||
|
end
|
||||||
|
|
||||||
|
enum Size
|
||||||
|
Small = 9,
|
||||||
|
Medium = 13,
|
||||||
|
Large = 19
|
||||||
|
end
|
||||||
|
|
||||||
|
alias Board = Hash(Tuple(Int8, Int8), Color)
|
||||||
|
|
||||||
|
class Game
|
||||||
|
property size : Size
|
||||||
|
property black_pass : String
|
||||||
|
property white_pass : String
|
||||||
|
property board : Board
|
||||||
|
property turn : Color
|
||||||
|
property sockets : Array(HTTP::WebSocket)
|
||||||
|
|
||||||
|
def initialize()
|
||||||
|
@size = Size::Small
|
||||||
|
@white_pass = ""
|
||||||
|
@black_pass = ""
|
||||||
|
@board = Board.new
|
||||||
|
@turn = Color::Black
|
||||||
|
@sockets = [] of HTTP::WebSocket
|
||||||
|
end
|
||||||
|
def initialize(size : Size, @black_pass, @white_pass)
|
||||||
|
@size = size
|
||||||
|
@board = Board.new
|
||||||
|
@turn = Color::Black
|
||||||
|
@sockets = [] of HTTP::WebSocket
|
||||||
|
end
|
||||||
|
|
||||||
|
private def cell_json(index, color, json)
|
||||||
|
json.object do
|
||||||
|
json.field "index" do
|
||||||
|
json.object do
|
||||||
|
json.field "x", index[0]
|
||||||
|
json.field "y", index[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
json.field "color", color.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def board_json(board, json)
|
||||||
|
json.array do
|
||||||
|
board.each do |key, value|
|
||||||
|
cell_json(key, value, json)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_json
|
||||||
|
JSON.build do |json|
|
||||||
|
json.object do
|
||||||
|
json.field "turn", @turn.to_s
|
||||||
|
json.field "board" { board_json(@board, json) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def count_neighbors(x, y, color, visited)
|
||||||
|
coord = {x, y}
|
||||||
|
if visited.includes?(coord) || (x < 0 || x >= @size.value || y < 0 || y >= @size.value)
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
visited.push(coord)
|
||||||
|
case @board[coord]?
|
||||||
|
when color
|
||||||
|
return count_neighbors(x - 1, y, color, visited) +
|
||||||
|
count_neighbors(x + 1, y, color, visited) +
|
||||||
|
count_neighbors(x, y - 1, color, visited) +
|
||||||
|
count_neighbors(x, y + 1, color, visited)
|
||||||
|
when nil
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
private def remove_color(x, y, color)
|
||||||
|
coord = {x, y}
|
||||||
|
if !(x < 0 || x >= @size.value || y < 0 || y >= @size.value) && @board[coord]? == color
|
||||||
|
@board.delete(coord)
|
||||||
|
remove_color(x - 1, y, color)
|
||||||
|
remove_color(x + 1, y, color)
|
||||||
|
remove_color(x, y - 1, color)
|
||||||
|
remove_color(x, y + 1, color)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def try_remove_branch(x, y, color)
|
||||||
|
coord = {x, y}
|
||||||
|
if @board[coord]? == color
|
||||||
|
neighbor_count = count_neighbors(x, y, color, [] of Tuple(Int8, Int8))
|
||||||
|
if neighbor_count == 0
|
||||||
|
remove_color(x, y, color)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def invert(color)
|
||||||
|
color == Color::Black ? Color::White : Color::Black
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(x, y, color)
|
||||||
|
coord = {x, y}
|
||||||
|
if @turn == color
|
||||||
|
@board[coord] = color
|
||||||
|
new_color = invert(color)
|
||||||
|
try_remove_branch(x - 1, y, new_color)
|
||||||
|
try_remove_branch(x + 1, y, new_color)
|
||||||
|
try_remove_branch(x, y - 1, new_color)
|
||||||
|
try_remove_branch(x, y + 1, new_color)
|
||||||
|
try_remove_branch(x, y, color)
|
||||||
|
@turn = new_color
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def color_char(color)
|
||||||
|
color == Color::Black ? 'B' : 'W'
|
||||||
|
end
|
||||||
|
|
||||||
|
private def board_string(board)
|
||||||
|
String.build do |str|
|
||||||
|
(0...@size.value).each do |x|
|
||||||
|
(0...@size.value).each do |y|
|
||||||
|
color = @board[{x.to_i8, y.to_i8}]?
|
||||||
|
char = color ? color_char(color) : 'E'
|
||||||
|
str << char
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def encode
|
||||||
|
{ @turn, @size.value, @white_pass.to_s, @black_pass.to_s, board_string(@board) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
3
src/Go/version.cr
Normal file
3
src/Go/version.cr
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module Go
|
||||||
|
VERSION = "0.1.0"
|
||||||
|
end
|
13
src/Go/views/base.ecr
Normal file
13
src/Go/views/base.ecr
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="/css/main.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Indie+Flower|Raleway" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<h1>Go</h1>
|
||||||
|
<%= content %>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
11
src/Go/views/game.ecr
Normal file
11
src/Go/views/game.ecr
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<div id="elm-root" class="<%= black ? "black" : "white" %>-player"></div>
|
||||||
|
<script src="/js/Go.js"></script>
|
||||||
|
<script>
|
||||||
|
var node = document.getElementById('elm-root')
|
||||||
|
var app = Elm.Main.embed(node, {
|
||||||
|
'black' : <%= black %>,
|
||||||
|
'url' : "<%= "ws://" + URL + ":" + PORT %>",
|
||||||
|
'id' : "<%= id %>",
|
||||||
|
'size' : <%= size %>
|
||||||
|
})
|
||||||
|
</script>
|
29
src/Go/views/index.ecr
Normal file
29
src/Go/views/index.ecr
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<div class="split-wrapper">
|
||||||
|
<div class="split-item">
|
||||||
|
<h2>Create Game</h2>
|
||||||
|
<form autocomplete="off" id="create-form" action="/create" method="post">
|
||||||
|
<input required placeholder="Session Name" type="text" name="id"></input>
|
||||||
|
<input required placeholder="Your Password" type="text" name="your-password"></input>
|
||||||
|
<input required placeholder="Their Password" type="text" name="their-password"></input>
|
||||||
|
<div class="radio-parent">
|
||||||
|
<div class="radio-wrapper">
|
||||||
|
<input type="radio" name="color" id="radio-black" value="black" checked></input>
|
||||||
|
<label for="radio-black">Black</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio-wrapper">
|
||||||
|
<input type="radio" name="color" id="radio-white" value="white"></input>
|
||||||
|
<label for="radio-white">White</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Create Game"></input>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="split-item">
|
||||||
|
<h2>Join Game</h2>
|
||||||
|
<form autocomplete="off" id="join-form" action="/game" method="post">
|
||||||
|
<input required placeholder="Session Name" type="text" name="id"></input>
|
||||||
|
<input required placeholder="Your Password" type="text" name="password"></input>
|
||||||
|
<input type="submit" value="Join Game"></input>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
Loading…
Reference in New Issue
Block a user