initial commit
This commit is contained in:
105
.gitignore
vendored
Normal file
105
.gitignore
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
|
||||
# Created by https://www.gitignore.io/api/python
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
.pytest_cache/
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule.*
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
.idea/
|
||||
# End of https://www.gitignore.io/api/python
|
||||
100
camel_race.py
Normal file
100
camel_race.py
Normal file
@@ -0,0 +1,100 @@
|
||||
from flask import request, url_for
|
||||
from flask_api import FlaskAPI, status, exceptions
|
||||
from typing import Union, Optional
|
||||
from random import choices
|
||||
|
||||
from game import CamelGame
|
||||
|
||||
app = FlaskAPI(__name__)
|
||||
|
||||
games = {}
|
||||
|
||||
allowed_id_chars = [chr(c) for c in range(0x41, 0x5B)] + [chr(c) for c in range(0x61, 0x7B)]
|
||||
allowed_id_chars.remove('l')
|
||||
allowed_id_chars.remove('I')
|
||||
|
||||
|
||||
def _generate_id():
|
||||
while True:
|
||||
new_id = ''.join(choices(allowed_id_chars, k=6))
|
||||
if new_id not in games:
|
||||
return new_id
|
||||
|
||||
|
||||
def _get_game_obj(game_id: str) -> Optional[CamelGame]:
|
||||
return games.get(game_id, None)
|
||||
|
||||
|
||||
@app.route('/game/<game_id>', methods=['GET'])
|
||||
def get_game(game_id: str):
|
||||
game = _get_game_obj(game_id)
|
||||
if game is None:
|
||||
return '', status.HTTP_404_NOT_FOUND
|
||||
|
||||
return {
|
||||
'status': game.state,
|
||||
'amount_left': game.side_length,
|
||||
'winning_ace': game.get_winner(),
|
||||
'left_cards': game.get_open_side_cards(),
|
||||
'aces': game.ace_position,
|
||||
'drinks': game.get_drinks(),
|
||||
'bets': game.bets,
|
||||
}
|
||||
|
||||
|
||||
@app.route('/game/<game_id>/start', methods=['GET'])
|
||||
def start_game(game_id: str):
|
||||
game = _get_game_obj(game_id)
|
||||
if game is None:
|
||||
return '', status.HTTP_404_NOT_FOUND
|
||||
|
||||
return {
|
||||
'started': game.start_game()
|
||||
}
|
||||
|
||||
|
||||
@app.route('/game/<game_id>/bet', methods=['POST'])
|
||||
def bet_on_game(game_id: str):
|
||||
game = _get_game_obj(game_id)
|
||||
if game is None:
|
||||
return '', status.HTTP_404_NOT_FOUND
|
||||
|
||||
user = request.data['USER']
|
||||
suit = request.data['SUIT']
|
||||
amount = int(request.data['AMOUNT'])
|
||||
|
||||
if game.bet(user, suit, amount):
|
||||
return {'ok': True}
|
||||
return {'ok': False}
|
||||
|
||||
|
||||
@app.route('/game/<game_id>/round', methods=['GET'])
|
||||
def do_round(game_id: str):
|
||||
game = _get_game_obj(game_id)
|
||||
if game is None:
|
||||
return '', status.HTTP_404_NOT_FOUND
|
||||
|
||||
card = game.do_round()
|
||||
|
||||
if card is None:
|
||||
return "game probably already done", status.HTTP_400_BAD_REQUEST
|
||||
|
||||
return {
|
||||
'card': card
|
||||
}
|
||||
|
||||
|
||||
@app.route('/game/', methods=['GET'])
|
||||
def start_new_game():
|
||||
game = CamelGame()
|
||||
game_id = _generate_id()
|
||||
games[game_id] = game
|
||||
game.init_game()
|
||||
return {
|
||||
'initialized': True,
|
||||
'url': url_for('get_game', game_id=game_id)
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
43
cli_race.py
Executable file
43
cli_race.py
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
from game import CamelGame
|
||||
|
||||
game = CamelGame()
|
||||
|
||||
if __name__ != '__main__':
|
||||
print("Not a module!")
|
||||
exit(1)
|
||||
|
||||
|
||||
def print_game():
|
||||
print(f'status: {game.state}')
|
||||
print(', '.join(game.get_open_side_cards()))
|
||||
for ace, pos in game.ace_position.items():
|
||||
print(" " * 4 * pos + ace)
|
||||
|
||||
|
||||
def print_drinks():
|
||||
drinks = game.get_drinks()
|
||||
if drinks is None:
|
||||
print("Not done yet!")
|
||||
return
|
||||
for user, amount in drinks.items():
|
||||
print(f"{user:16}{amount}")
|
||||
|
||||
|
||||
while True:
|
||||
inp = input("command: ")
|
||||
if inp == 'r':
|
||||
game.do_round()
|
||||
print_game()
|
||||
elif inp == 'i':
|
||||
game.init_game()
|
||||
elif inp.startswith('b '):
|
||||
_, u, s, a = inp.split()
|
||||
a = int(a)
|
||||
game.bet(u, s, a)
|
||||
elif inp == 's':
|
||||
game.start_game()
|
||||
elif inp == 'd':
|
||||
print_drinks()
|
||||
else:
|
||||
print("Unknown " + inp)
|
||||
131
game.py
Normal file
131
game.py
Normal file
@@ -0,0 +1,131 @@
|
||||
from random import shuffle
|
||||
from typing import List, Dict, Optional
|
||||
from collections import deque
|
||||
|
||||
nums = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
|
||||
suits = ['H', 'S', 'D', 'C']
|
||||
|
||||
|
||||
def get_deck(add_jokers: bool=False) -> deque:
|
||||
if add_jokers:
|
||||
jokers = ['J', 'J']
|
||||
else:
|
||||
jokers = []
|
||||
return deque([n + s for n in nums for s in suits] + jokers)
|
||||
|
||||
|
||||
def create_new_random_card_deck(add_jokers: bool=False) -> deque:
|
||||
deck = get_deck(add_jokers)
|
||||
shuffle(deck)
|
||||
return deck
|
||||
|
||||
|
||||
class CamelGame:
|
||||
deck: deque
|
||||
ace_position: dict
|
||||
|
||||
side_list: list
|
||||
side_length = 8
|
||||
side_open = 0
|
||||
|
||||
state: str
|
||||
|
||||
bets: List[Dict]
|
||||
|
||||
def __init__(self):
|
||||
self.deck = create_new_random_card_deck(False)
|
||||
for ace in ['AH', 'AS', 'AD', 'AC']:
|
||||
self.deck.remove(ace) # remove the aces
|
||||
|
||||
self.ace_position = {
|
||||
'AH': 0,
|
||||
'AS': 0,
|
||||
'AD': 0,
|
||||
'AC': 0,
|
||||
}
|
||||
|
||||
self.side_list = []
|
||||
|
||||
self.state = None
|
||||
self.bets = []
|
||||
|
||||
def init_game(self):
|
||||
for _ in range(self.side_length):
|
||||
self.side_list.append(self.deck.pop())
|
||||
|
||||
self.state = 'initialized'
|
||||
|
||||
def do_round(self) -> Optional[str]:
|
||||
if self.state != 'active':
|
||||
print("Game is not stated yet or already done")
|
||||
return None
|
||||
|
||||
card = self.popcard()
|
||||
|
||||
if any(v == self.side_length for v in self.ace_position.values()):
|
||||
self.state = 'done'
|
||||
|
||||
return card
|
||||
|
||||
def bet(self, user: str, suit: str, amount: int) -> bool:
|
||||
if self.state != 'initialized':
|
||||
return False
|
||||
if any(b['user'] == user for b in self.bets):
|
||||
return False
|
||||
if suit not in suits:
|
||||
return False
|
||||
self.bets.append({'user': user, 'suit': suit, 'amount': amount})
|
||||
return True
|
||||
|
||||
def start_game(self):
|
||||
if self.state != 'initialized':
|
||||
return False
|
||||
self.state = 'active'
|
||||
return True
|
||||
|
||||
def get_drinks(self) -> Optional[Dict]:
|
||||
if self.state != 'done':
|
||||
return None
|
||||
result = {}
|
||||
winner = self.get_winner()[-1]
|
||||
|
||||
drinks = sum(map(lambda b: b['amount'], filter(lambda b: b['suit'] == winner, self.bets)))
|
||||
|
||||
for bet in self.bets:
|
||||
if bet['suit'] == winner:
|
||||
mdrinks = 0
|
||||
else:
|
||||
mdrinks = drinks + bet['amount']
|
||||
result[bet['user']] = mdrinks
|
||||
|
||||
return result
|
||||
|
||||
def get_winner(self) -> Optional[str]:
|
||||
for card, pos in self.ace_position.items():
|
||||
if pos == self.side_length:
|
||||
return card
|
||||
return None
|
||||
|
||||
def get_open_side_cards(self) -> list:
|
||||
open_cards = self.side_list[:self.side_open]
|
||||
return open_cards + ['XX'] * (self.side_length - self.side_open)
|
||||
|
||||
def popcard(self) -> str:
|
||||
next_card = self.deck.pop()
|
||||
move_card = 'A' + next_card[-1]
|
||||
|
||||
self.ace_position[move_card] += 1
|
||||
|
||||
self.check_and_open_side()
|
||||
|
||||
return move_card
|
||||
|
||||
def check_and_open_side(self):
|
||||
if not all(v > self.side_open for v in self.ace_position.values()):
|
||||
return
|
||||
|
||||
opened_card = self.side_list[self.side_open]
|
||||
moved_ace = 'A' + opened_card[-1]
|
||||
self.ace_position[moved_ace] = self.side_open
|
||||
|
||||
self.side_open += 1
|
||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Flask
|
||||
Flask-API
|
||||
48
static/css/reset.css
Normal file
48
static/css/reset.css
Normal file
@@ -0,0 +1,48 @@
|
||||
/* http://meyerweb.com/eric/tools/css/reset/
|
||||
v2.0 | 20110126
|
||||
License: none (public domain)
|
||||
*/
|
||||
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
blockquote, q {
|
||||
quotes: none;
|
||||
}
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
26
static/css/style.css
Normal file
26
static/css/style.css
Normal file
@@ -0,0 +1,26 @@
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%; }
|
||||
|
||||
h1 {
|
||||
font-size: xx-large;
|
||||
font-weight: bolder; }
|
||||
|
||||
#topcards > .card {
|
||||
display: inline-block; }
|
||||
|
||||
.card-ace {
|
||||
position: relative;
|
||||
-webkit-transition: left 0.25s;
|
||||
-moz-transition: left 0.25s;
|
||||
-ms-transition: left 0.25s;
|
||||
-o-transition: left 0.25s;
|
||||
transition: left 0.25s;
|
||||
display: inline; }
|
||||
|
||||
.card {
|
||||
height: 100px; }
|
||||
.card svg {
|
||||
height: 100px; }
|
||||
|
||||
/*# sourceMappingURL=style.css.map */
|
||||
7
static/css/style.css.map
Normal file
7
static/css/style.css.map
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"mappings": "AAAA,UAAU;EACR,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI;;AAEd,EAAE;EACA,SAAS,EAAE,QAAQ;EACnB,WAAW,EAAE,MAAM;;AAErB,iBAAiB;EACf,OAAO,EAAE,YAAY;;AAEvB,SAAS;EACP,QAAQ,EAAE,QAAQ;EAClB,kBAAkB,EAAE,UAAU;EAC9B,eAAe,EAAE,UAAU;EAC3B,cAAc,EAAE,UAAU;EAC1B,aAAa,EAAE,UAAU;EACzB,UAAU,EAAE,UAAU;EACtB,OAAO,EAAE,MAAM;;AAEjB,KAAK;EACH,MAAM,EAAE,KAAK;EACb,SAAG;IACD,MAAM,EAAE,KAAK",
|
||||
"sources": ["style.sass"],
|
||||
"names": [],
|
||||
"file": "style.css"
|
||||
}
|
||||
24
static/css/style.sass
Normal file
24
static/css/style.sass
Normal file
@@ -0,0 +1,24 @@
|
||||
html, body
|
||||
width: 100%
|
||||
height: 100%
|
||||
|
||||
h1
|
||||
font-size: xx-large
|
||||
font-weight: bolder
|
||||
|
||||
#topcards > .card
|
||||
display: inline-block
|
||||
|
||||
.card-ace
|
||||
position: relative
|
||||
-webkit-transition: left 0.25s
|
||||
-moz-transition: left 0.25s
|
||||
-ms-transition: left 0.25s
|
||||
-o-transition: left 0.25s
|
||||
transition: left 0.25s
|
||||
display: inline
|
||||
|
||||
.card
|
||||
height: 100px
|
||||
svg
|
||||
height: 100px
|
||||
236
static/index.html
Normal file
236
static/index.html
Normal file
@@ -0,0 +1,236 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Card Game</title>
|
||||
<script src="js/jquery-3.3.1.min.js"></script>
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
|
||||
<link rel="stylesheet" href="css/reset.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<script>
|
||||
function cardToPlayCard(card) {
|
||||
let pre, post;
|
||||
|
||||
if (card === 'XX') {
|
||||
return "svg/svg-cards.svg#back";
|
||||
}
|
||||
|
||||
switch (card[0]) {
|
||||
case 'A':
|
||||
pre = '1';
|
||||
break;
|
||||
case 'K':
|
||||
pre = 'king';
|
||||
break;
|
||||
case 'Q':
|
||||
pre = 'queen';
|
||||
break;
|
||||
case 'J':
|
||||
pre = 'jack';
|
||||
break;
|
||||
default:
|
||||
pre = card.substr(0, card.length - 1);
|
||||
}
|
||||
switch (card.substr(card.length - 1)) {
|
||||
case 'H':
|
||||
post = 'heart';
|
||||
break;
|
||||
case 'S':
|
||||
post = 'spade';
|
||||
break;
|
||||
case 'D':
|
||||
post = 'diamond';
|
||||
break;
|
||||
case 'C':
|
||||
post = 'club';
|
||||
break;
|
||||
}
|
||||
|
||||
return `svg/svg-cards.svg#${pre}_${post}`
|
||||
|
||||
}
|
||||
|
||||
$(function () {
|
||||
let btnNewGame = $("#newgame");
|
||||
let btnStartGame = $("#startgame");
|
||||
let btnDoRound = $("#doround");
|
||||
|
||||
let txtBetName = $("#bet-name");
|
||||
let selBetSuit = $("#bet-suit");
|
||||
let numBetAmount = $("#bet-amount");
|
||||
let btnBet = $("#bet");
|
||||
|
||||
let aces = {
|
||||
'AS': $("#card-ace-spades"),
|
||||
'AH': $("#card-ace-hearts"),
|
||||
'AD': $("#card-ace-diamonds"),
|
||||
'AC': $("#card-ace-clubs"),
|
||||
};
|
||||
|
||||
let templateCard = $("#template-card").html();
|
||||
|
||||
let topcards = $("#topcards");
|
||||
|
||||
let gameBase = null;
|
||||
|
||||
function buildTopCards(cards) {
|
||||
let html;
|
||||
topcards.empty();
|
||||
cards.forEach(function (card, ind, array) {
|
||||
console.log(card, ind, array);
|
||||
html = templateCard;
|
||||
html = html.replace("{href}", cardToPlayCard(card));
|
||||
topcards.append($(html));
|
||||
});
|
||||
}
|
||||
|
||||
function updateGame() {
|
||||
$.ajax({
|
||||
'url': gameBase
|
||||
}).done(function (data) {
|
||||
console.log(data);
|
||||
|
||||
buildTopCards(data['left_cards']);
|
||||
|
||||
$.each(data['aces'], function (key, value) {
|
||||
aces[key].css({'left': value * 150});
|
||||
});
|
||||
// todo check if game is done
|
||||
}).fail(function (data) {
|
||||
alert(data);
|
||||
console.log(data);
|
||||
})
|
||||
}
|
||||
|
||||
btnNewGame.click(function () {
|
||||
$.ajax({
|
||||
'url': '/game/'
|
||||
}).done(function (data) {
|
||||
gameBase = data['url'];
|
||||
updateGame();
|
||||
}).fail(function (data) {
|
||||
alert(data);
|
||||
console.log(data);
|
||||
});
|
||||
});
|
||||
|
||||
btnStartGame.click(function () {
|
||||
$.ajax({
|
||||
'url': gameBase + '/start'
|
||||
}).done(function (data) {
|
||||
updateGame();
|
||||
}).fail(function (data) {
|
||||
alert(data);
|
||||
console.log(data);
|
||||
});
|
||||
});
|
||||
|
||||
btnDoRound.click(function () {
|
||||
$.ajax({
|
||||
'url': gameBase + '/round'
|
||||
}).done(function (data) {
|
||||
console.log(data); // todo display card
|
||||
updateGame();
|
||||
}).fail(function (data) {
|
||||
alert(data);
|
||||
console.log(data);
|
||||
});
|
||||
});
|
||||
|
||||
btnBet.click(function () {
|
||||
let user, suit, amount;
|
||||
user = txtBetName.val();
|
||||
suit = selBetSuit.val();
|
||||
amount = numBetAmount.val();
|
||||
|
||||
$.ajax({
|
||||
'url': gameBase + '/bet',
|
||||
'method': 'POST',
|
||||
'data': {
|
||||
'USER': user,
|
||||
'SUIT': suit,
|
||||
'AMOUNT': amount,
|
||||
}
|
||||
}).done(function (data) {
|
||||
console.log(data);
|
||||
}).fail(function (data) {
|
||||
alert(data);
|
||||
});
|
||||
|
||||
txtBetName.val("");
|
||||
numBetAmount.val("")
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<nav id="header">
|
||||
<h1>Kamelen Race!</h1>
|
||||
</nav>
|
||||
<div id="game">
|
||||
<div id="control">
|
||||
<div>
|
||||
<button id="newgame">New Game</button>
|
||||
<button id="startgame">Start Game</button>
|
||||
<button id="doround">Do Round</button>
|
||||
</div>
|
||||
<div>
|
||||
<h1>Bet</h1>
|
||||
<input type="text" id="bet-name" placeholder="Name">
|
||||
<label for="bet-suit">Suit</label>
|
||||
<select id="bet-suit">
|
||||
<option value="S">Spades</option>
|
||||
<option value="H">Hearts</option>
|
||||
<option value="D">Diamonds</option>
|
||||
<option value="C">Clubs</option>
|
||||
</select>
|
||||
<input type="number" placeholder="Amount" id="bet-amount">
|
||||
<button id="bet">Bet</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="topcards">
|
||||
</div>
|
||||
<div id="track">
|
||||
<div class="tracklane">
|
||||
<div id="card-ace-spades" class="card card-ace">
|
||||
<svg width="150" viewBox="0 0 262 180">
|
||||
<use href="svg/svg-cards.svg#1_spade" transform="rotate(90) translate(0,-260)"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tracklane">
|
||||
<div id="card-ace-hearts" class="card card-ace">
|
||||
<svg width="150" viewBox="0 0 262 180">
|
||||
<use href="svg/svg-cards.svg#1_heart" transform="rotate(90) translate(0,-260)"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tracklane">
|
||||
<div id="card-ace-diamonds" class="card card-ace">
|
||||
<svg width="150" viewBox="0 0 262 180">
|
||||
<use href="svg/svg-cards.svg#1_diamond" transform="rotate(90) translate(0,-260)"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tracklane">
|
||||
<div id="card-ace-clubs" class="card card-ace">
|
||||
<svg width="150" viewBox="0 0 262 180">
|
||||
<use href="svg/svg-cards.svg#1_club" transform="rotate(90) translate(0,-260)"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script language="html/template" id="template-card">
|
||||
<div class="card">
|
||||
<svg width="150" viewBox="0 0 262 180">
|
||||
<use href="{href}" transform="rotate(90) translate(0,-260)"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
10364
static/js/jquery-3.3.1.js
vendored
Normal file
10364
static/js/jquery-3.3.1.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
static/js/jquery-3.3.1.min.js
vendored
Normal file
2
static/js/jquery-3.3.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/js/jquery-3.3.1.min.map
Normal file
1
static/js/jquery-3.3.1.min.map
Normal file
File diff suppressed because one or more lines are too long
2
static/svg/svg-cards.svg
Normal file
2
static/svg/svg-cards.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 941 KiB |
Reference in New Issue
Block a user