Firs commit

This commit is contained in:
Léo 2020-04-25 12:08:19 +02:00
commit b8e4dc2a5d
73 changed files with 2571 additions and 0 deletions

35
.gitignore vendored Normal file
View File

@ -0,0 +1,35 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# Translations
*.mo
*.pot
# pyenv
.python-version
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
## Editors:
*.bak
*.sav
*.backup
.*.swp
*~
# Secret configuration
settings.cfg
# Personal conf
static/jquery.js
static/socket.io.js
templates/head-perso.html
templates/scripts-perso.html

373
belote.py Normal file
View File

@ -0,0 +1,373 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import flask
from flask_socketio import SocketIO
import flask_login as fll
import datetime
from db import db, User, Game, Player, levels_users
from belote_jeu import N_TURN, N_PLAYERS
import belote_ws
login_manager = fll.LoginManager()
app = flask.Flask(__name__)
app.config.from_pyfile('settings.cfg')
#######################################################################
# Plugin management #
#######################################################################
# SocketIO
socketio = SocketIO(app)
login_manager.init_app(app)
# Database management
db.app = app
db.init_app(app)
db.create_all()
db.session.commit()
# Insert admin if not exist
if User.query.limit(1).first() is None:
admin = User(login='admin', name='Admin', level=levels_users['Admin'])
admin.set_password('admin')
db.session.add(admin)
db.session.commit()
for i in range(4):
if User.query.get('leo'+str(i)) is None:
leo = User(login='leo'+str(i), name='Leo '+str(i), level=levels_users['Can manage simple users'])
leo.set_password('leo')
db.session.add(leo)
db.session.commit()
# Login management
@login_manager.user_loader
def load_user(user_id):
if user_id is None :
return None
return User.query.get(user_id)
@login_manager.unauthorized_handler
def unauthorized_callback():
return flask.redirect('/login?next=' + flask.request.path)
#######################################################################
# Global variables or fuctions #
#######################################################################
URL_DEFAULT = '/games'
namespaces_join = {}
namespaces = {}
def create_namespace(gid, join=False):
if(join):
namespace = belote_ws.CustomNamespaceJoin(gid)
namespaces_join[gid] = True
socketio.on_namespace(namespace)
else:
namespace = belote_ws.CustomNamespacePlay(gid)
namespaces[gid] = True
socketio.on_namespace(namespace)
@app.route('/')
def home():
user = fll.current_user
if not hasattr(user, 'level'):
user = None
return flask.render_template("index.html", user=user)
#######################################################################
# Login, logout #
#######################################################################
@app.route('/login', methods=['GET', 'POST'])
def login():
if fll.current_user:
flask.redirect(URL_DEFAULT)
if flask.request.method=="POST":
username = flask.request.form['username']
password = flask.request.form['password']
user = User.query.get(username)
if user and user.level > levels_users['Inactive'] and user.check_password(password):
fll.login_user(user)
user.last_login = datetime.datetime.utcnow()
next = flask.request.args.get('next')
if next is not None and next[0]=='/':
return flask.redirect(next)
else:
return flask.redirect(URL_DEFAULT)
else:
app.logger.warning('Login fail for user {} from {}'.format(user, flask.request.remote_addr))
flask.flash('Invalid username/password combination')
return flask.render_template('login.html', user=None)
@app.route('/logout')
def logout():
fll.logout_user()
return flask.redirect('/')
#######################################################################
# Games : list #
#######################################################################
@app.route('/games')
@fll.login_required
def games():
games = Game.query.all()
mesjeux = []
joinable = []
for g in games:
if g.admin==fll.current_user.login:
mesjeux.append(g)
elif g.can_join(fll.current_user):
joinable.append(g)
cancreategames = fll.current_user.level >= levels_users['Can create games']
candeletegames = fll.current_user.level >= levels_users['Admin']
return flask.render_template('games.html', mesjeux=mesjeux, joinable=joinable, user=fll.current_user, \
cancreategames=cancreategames, candeletegames=candeletegames)
#######################################################################
# Create, join and play to a game #
#######################################################################
@app.route('/add_game', methods=['GET', 'POST'])
@fll.login_required
def game_add():
player = fll.current_user
if fll.current_user.level >= levels_users['Can create games']:
if flask.request.method=="POST":
name = flask.request.form['name']
if name is None or name == '':
return flask.redirect('/add_game')
game = Game(name=name, admin=player.login)
db.session.add(game)
db.session.commit()
game.join(fll.current_user)
db.session.commit()
return flask.redirect('/game/{}'.format(game.id))
return flask.render_template('game_add.html', user=fll.current_user)
return flask.redirect(URL_DEFAULT)
@app.route('/game/<roomid>/join')
@fll.login_required
def game_page_join(roomid):
game = Game.query.get(roomid)
if game:
player = fll.current_user
if game.start:
return flask.redirect('/game/{}'.format(roomid))
if game.can_join(player):
if game.id not in namespaces_join:
create_namespace(game.id, join=True)
return flask.render_template('game_join.html', game=game, user=player, admin=game.admin)
return flask.redirect(URL_DEFAULT)
@app.route('/game/<roomid>/start')
@fll.login_required
def game_page_start(roomid):
game = Game.query.get(roomid)
if game:
player = fll.current_user
if game.isadmin(player) and game.fixplayers and not game.start:
return flask.render_template('game_start.html', game=game, user=player)
return flask.redirect('/game/{}'.format(roomid))
return flask.redirect(URL_DEFAULT)
@app.route('/game/<roomid>')
@fll.login_required
def game_page(roomid):
game = Game.query.get(roomid)
if game:
player = fll.current_user
if not game.start:
return flask.redirect('/game/{}/join'.format(roomid))
if game.authorized(player) :
if game.id not in namespaces:
create_namespace(game.id)
return flask.render_template('game.html', game=game, user=player)
return flask.redirect(URL_DEFAULT)
@app.route('/game/<roomid>/del')
@fll.login_required
def delgame(roomid):
game = Game.query.get(roomid)
if game is not None and fll.current_user.login == game.admin:
confirm = flask.request.args.get('confirm')
if confirm is not None:
db.session.delete(game)
db.session.commit()
else:
return flask.render_template('game_del.html', game=game, user=fll.current_user)
return flask.redirect(URL_DEFAULT)
#######################################################################
# Users management #
#######################################################################
@app.route('/admin/users')
@fll.login_required
def admin_users():
if fll.current_user.level < levels_users['Can manage simple users']:
return flask.redirect(URL_DEFAULT)
users = User.query.all()
return flask.render_template('admin_users.html', user=fll.current_user, users=users)
@app.route('/admin/users/<user>', methods=['GET', 'POST'])
@fll.login_required
def edituser(user):
new = user=='new'
user = User.query.get(user)
if not new and user is None:
return flask.redirect('/admin/users')
if fll.current_user.level >= levels_users['Admin'] or \
fll.current_user.level >= levels_users['Can manage simple users'] and \
fll.current_user.level > user.level:
if flask.request.method=="POST":
username = flask.request.form['username']
name = flask.request.form['name']
password = flask.request.form['password']
level = int(flask.request.form['level'])
if level not in levels_users.values():
flask.flash('Level unknown')
return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
if fll.current_user.level < levels_users['Admin']:
if not level < fll.current_user.level:
flask.flash('Invalid level')
return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
if len(password)<2 and new:
flask.flash('Mot de passe trop court')
return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
if new and User.query.get(username) is not None:
flask.flash('Login déjà utilisé ! Choisissez un autre !')
return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
if new:
usertoadd = User(login=username, name=name, level=level)
usertoadd.set_password(password)
db.session.add(usertoadd)
else:
user.name = name
if len(password)>=2:
user.password = password
user.level = level
db.session.commit()
return flask.redirect('/admin/users')
else:
return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
return flask.redirect(URL_DEFAULT)
@app.route('/admin/users/<user>/del')
@fll.login_required
def deluser(user):
user = User.query.get(user)
if user is None:
return flask.redirect('/admin/users')
if fll.current_user.level >= levels_users['Admin'] or \
fll.current_user.level >= levels_users['Can manage simple users'] and \
fll.current_user.level > user.level:
confirm = flask.request.args.get('confirm')
if confirm is not None:
delete_game(user=user)
db.session.delete(user)
db.session.commit()
return flask.redirect('/admin/users')
return flask.render_template("admin_user_del.html", user=fll.current_user, useredit=user)
@app.route('/password', methods=['GET', 'POST'])
@fll.login_required
def change_password():
if flask.request.method=="POST":
password = flask.request.form['password']
if len(password) < 5:
flask.flash('Mot de passe trop court !')
else:
fll.current_user.set_password(password)
db.session.commit()
return flask.redirect(URL_DEFAULT)
return flask.render_template("password.html", user=fll.current_user)
#######################################################################
# Game managament #
#######################################################################
@app.route('/admin/games')
@fll.login_required
def admin_games():
if fll.current_user.level < levels_users['Can manage games']:
return flask.redirect(URL_DEFAULT)
games = Game.query.all()
return flask.render_template('admin_games.html', user=fll.current_user, games=games)
@app.route('/admin/games/<roomid>/del')
@fll.login_required
def admin_game_del(roomid):
game = Game.query.get(roomid)
if game is None:
return flask.redirect('/admin/games')
if fll.current_user.level >= levels_users['Can manage games']:
confirm = flask.request.args.get('confirm')
if confirm is not None:
delete_game(game=game)
return flask.redirect('/admin/games')
else:
return flask.render_template('game_del.html', user=fll.current_user, game=game)
return flask.redirect(URL_DEFAULT)
@app.route('/game/<roomid>/see')
@fll.login_required
def see_game(roomid):
game = Game.query.get(roomid)
if game is None:
return flask.redirect('/games')
if fll.current_user.level >= levels_users['Can manage games'] or game.authorized(fll.current_user) and game.turn==N_TURN*N_PLAYERS:
return game.serialize_state_anonymous()
return flask.redirect(URL_DEFAULT)
@app.route('/admin/games/new', methods=['GET', 'POST'])
@fll.login_required
def admin_games_new():
if fll.current_user.level < levels_users['Can manage games']:
return flask.redirect(URL_DEFAULT)
if flask.request.method=="POST":
name = flask.request.form['name']
admin = flask.request.form['admin']
admin = User.query.get(admin)
if admin is None:
flask.flash('Login inconnu')
elif len(name)<2:
flask.flash('Nom trop court !')
else:
game = Game(name=name, admin=admin.login)
db.session.add(game)
db.session.commit()
game.join(admin)
db.session.commit()
return flask.redirect('/admin/games')
return flask.render_template('admin_game_add.html', user=fll.current_user)
def delete_game(user=None, game=None):
if user is not None:
games = Game.query.filter_by(admin=user.login).all()
for g in games:
for p in g.players:
db.session.delete(p)
db.session.delete(g)
db.session.commit()
if isinstance(game, int):
g = Game.query.get(game)
if g is not None:
for p in g.players:
db.session.delete(p)
db.session.delete(g)
db.session.commit()
elif game is not None:
for p in game.players:
db.session.delete(p)
db.session.delete(game)
db.session.commit()
if __name__ == '__main__':
socketio.run(app, debug=True, host='0.0.0.0')

8
belote.wsgi Normal file
View File

@ -0,0 +1,8 @@
import os
import sys
os.chdir(os.path.dirname(__file__))
sys.path.insert(0,os.path.dirname(__file__)+'/venv/lib/python{}.{}/site-packages'.format(sys.version_info.major, sys.version_info.minor))
sys.path.insert(0,os.path.dirname(__file__))
from belote import app as application

148
belote_jeu.py Normal file
View File

@ -0,0 +1,148 @@
# -*- coding: utf-8 -*-
N_PLAYERS = 4
N_TURN = int(32/4)
# P : pique, C : Coeur, F : Carreau, T : Trèfle
# 7..9, 0 puis VDRA
cards =['AP', 'RP', 'DP', 'VP', '0P', '9P', '8P', '7P',
'AC', 'RC', 'DC', 'VC', '0C', '9C', '8C', '7C',
'AF', 'RF', 'DF', 'VF', '0F', '9F', '8F', '7F',
'AT', 'RT', 'DT', 'VT', '0T', '9T', '8T', '7T'
]
points_atout = {'V' : 20, '9' : 14, 'A': 11, '0': 10, 'R': 4, 'D': 3, '8':0, '7':0}
points_atout_order = {'V' : 20, '9' : 14, 'A': 11, '0': 10, 'R': 4, 'D': 3, '8':1, '7':0}
points_order = {'A': 11, '0': 10, 'R': 4, 'D': 3, 'V': 2, '9': 1, '8':0.5, '7':0}
points = {'A': 11, '0': 10, 'R': 4, 'D': 3, 'V': 2, '9': 0, '8':0, '7':0}
translate = {'A': 'As', 'R' : 'Roi', 'D': 'Dame', 'V': 'Valet', '0': '10', 'P': 'Pique', 'C': 'Coeur', 'T':'Trèfle', 'F':'Carreau'}
colors = ['P', 'C', 'F', 'T']
def translator(card):
s = []
for c in card:
if c in translate:
s.append(translate[c])
else:
s.append(c)
return ' '.join(s)
def winner(cards, atout):
"""
Returns id winner (0-3), number of points
"""
if isinstance(cards, str):
cardsl = cards.split(',')
cardss = cards
else:
cardss = ''.join(cards)
cardsl = cards
if len(cardss)==0:
return 0, 0
winner = None
if atout in cardss:
for i in range(len(cardsl)):
if cardsl[i][1] == atout:
if winner is None or points_atout_order[cardsl[i][0]] > points_atout_order[cardsl[winner][0]]:
winner = i
if winner is None:
couldemandee = cardss[1]
for i in range(len(cardsl)):
if cardsl[i][1] == couldemandee:
if winner is None or points_order[cardsl[i][0]] > points_order[cardsl[winner][0]]:
winner = i
pointsr = 0
for c in cardsl:
if c[1] == atout:
pointsr += points_atout[c[0]]
else:
pointsr += points[c[0]]
return winner, pointsr
def legal_play(played, atout, card, main):
if card not in cards:
return False
if card not in main:
return False
if played is None :
playedl = []
playeds = ''
elif isinstance(played, str):
playedl = played.split(',')
playeds = played
else:
playeds = ''.join(played)
playedl = played
if main is None or main=='':
main = []
if isinstance(main, str):
mains = main
main = main.split(',')
else:
mains = ','.join(main)
# Le premier a jouer fait ce qu'il veut
if len(playeds)==0:
return True
couldemandee =playeds[1]
# On regarde les atouts pour plus tard:
maxpose = -1
for c in playedl:
if c[1]==atout and points_atout_order[c[0]]>maxpose:
maxpose = points_atout_order[c[0]]
peutmonter = False
has_atout = False
for m in main: #Peut-on monter ?
if m[1] == atout:
has_atout = True
if points_atout_order[m[0]]>maxpose:
peutmonter = True
break
# Il faut jouer de la couleur demandée
if couldemandee != atout:
if couldemandee in mains:
if card[1]== couldemandee:
return True
else:
return False
if couldemandee == atout:
# Il faut jouer atout (si possible)
if has_atout:
if card[1] != atout:
return False
# Et monter ! (si possible)
if peutmonter:
if points_atout_order[card[0]]<=maxpose:
return False
# Mais si on a pas, tant pis !
return True
# Sinon, mon partenaire est-il maitre ?
if len(playedl)>=2:
maitre, xxx = winner(playedl, atout)
if maitre == len(playedl)-2 :
# Si oui, jeu libre
return True
# Sinon, ai-je de l'atout ?
if has_atout:
if card[1] != atout:
return False
# Si oui, je dois monter si possible
if peutmonter:
if points_atout_order[card[0]] < maxpose:
return False
return True
# Sinon, jeu libre !
return True

168
belote_ws.py Normal file
View File

@ -0,0 +1,168 @@
# -*- coding: utf-8 -*-
import flask_socketio as fio
from flask_login import current_user
from db import Game, Player, User
from belote_jeu import translator, legal_play, colors, N_PLAYERS, N_TURN
NAME_ARBITRE = 'Arbitre'
NAME_BONJOUR = 'Portier'
class CustomNamespacePlay(fio.Namespace):
def __init__(self, id):
self._id = id
self.users = []
fio.Namespace.__init__(self, '/game/{}'.format(id))
def on_connect(self):
if current_user is None or not self.game().authorized(current_user):
fio.disconnect()
if current_user.name not in self.users:
if len(self.users)==1:
fio.emit('text', {'text': '{} est là !'.format(self.users[0]), 'username':None, 'name':NAME_BONJOUR})
elif len(self.users)>1:
fio.emit('text', {'text': '{} est là !'.format(', '.join(self.users)), 'username':None, 'name':NAME_BONJOUR})
self.users.append(current_user.name)
fio.emit('text', {'text': '{} vient d\'arriver !'.format(current_user.name), 'username':None, 'name':NAME_BONJOUR}, broadcast=True)
pass
def on_disconnect(self):
if current_user.name in self.users:
self.users.remove(current_user.name)
fio.emit('text', {'text': '{} est parti.'.format(current_user.name), 'username':None, 'name':NAME_BONJOUR}, broadcast=True)
pass
def on_join(self, data):
pass
def on_choose_color(self, data):
if 'atout' not in data:
return
atout = data['atout']
game = self.game()
if not game.turn<0:
fio.emit('text', {'text': "Choix d'atout indisponible", 'username':None, 'name':NAME_ARBITRE})
return
# C'est mon tour ?
if game.get_nr(current_user) != (game.turn + game.first_player)%4:
fio.emit('text', {'text': "Ce n'est pas votre tour...", 'username':None, 'name':NAME_ARBITRE})
return
# Choix légal pour ce tour ?
if atout is not None and game.turn < -4 and game.played[1] != atout:
fio.emit('text', {'text': "Pour l'instant on prend ou pas, mais on ne peut pas choisir l'atout.", 'username':None, 'name':NAME_ARBITRE})
return
if atout is not None and not atout in colors:
fio.emit('text', {'text': "Couleur d'atout inconnue ({})".format(atout), 'username':None, 'name':NAME_ARBITRE})
return
# OK !
print('atout choix')
print(atout)
if(game.tour_choix(atout, current_user)):
print('Emit atout choix')
fio.emit('choose_color', {'turn':game.turn, 'first_player':game.first_player, 'atout':game.atout, 'preneur':game.preneur if game.preneur is not None else current_user.login}, broadcast=True)
else:
fio.emit('text', {'text': "Erreur E0101", 'username':None, 'name':NAME_ARBITRE})
def on_play(self, data):
if 'card' not in data:
return
card = data['card']
game = self.game()
if game.turn<0:
fio.emit('text', {'text': "Jeu pas encore disponible. Il faut choisir l'atout !", 'username':None, 'name':NAME_ARBITRE})
return
# C'est mon tour ?
if game.get_nr(current_user) != (game.turn + game.first_player)%4:
fio.emit('text', {'text': "Ce n'est pas votre tour...", 'username':None, 'name':NAME_ARBITRE})
return
# Choix légal pour ce tour ?
p = game.get_player(current_user)
if legal_play(game.played, game.atout, card, p.cards):
ok, winnr = game.tour_jeu(card, current_user)
if ok:
fio.emit('play', {'turn':game.turn, 'played':game.played.split(',') if game.played is not None and game.played !='' else [], \
'last_played':game.last_played.split(',') if game.last_played is not None else [] , 'first_player': game.first_player, 'points':[game.points_0, game.points_1]}, broadcast=True)
else:
fio.emit('text', {'text': "Vous ne pouvez pas jouer cette carte ({})".format(translator(card)), 'username':None, 'name':NAME_ARBITRE})
return
def on_text(self, data):
if 'text' in data and len(data['text'])>0:
fio.emit('text', {'text':data['text'], 'username':current_user.login, 'name':current_user.name}, broadcast=True);
def on_monjeu(self, data):
cards = self.game().get_player(current_user).cards
cards = cards.split(',') if cards is not None and cards != '' else []
fio.emit('monjeu', {'cards':cards});
def on_restart(self, data):
game = self.game()
if game.isadmin(current_user):
if game.turn==N_TURN*N_PLAYERS:
game.restart_jeu()
fio.emit('restart', game.serialize_state_anonymous(), broadcast=True)
else:
fio.emit('text', {'text': "Finissez la partie avant d'en recommencer une.", 'username':None, 'name':NAME_ARBITRE})
else:
fio.emit('text', {'text': "Demandez au maitre du jeu pour reprendre une partie.", 'username':None, 'name':NAME_ARBITRE})
def game(self):
return Game.query.get(self._id)
class CustomNamespaceJoin(fio.Namespace):
def __init__(self, id):
self._id = id
fio.Namespace.__init__(self, '/game/{}/join'.format(id))
def on_connect(self):
pass
def on_disconnect(self):
pass
def on_join(self, data):
if self.game().can_join(current_user):
if not self.game().authorized(current_user):
self.game().join(current_user)
fio.emit('join', {'username':current_user.login, 'name': current_user.name}, broadcast=True)
def on_add_player(self, data):
if self.game().isadmin(current_user):
if 'username' in data:
user = User.query.get(data['username'])
if user is not None and self.game().can_join(user):
if not self.game().authorized(user):
self.game().join(user)
fio.emit('join', {'username':user.login, 'name': user.name}, broadcast=True)
def on_leave(self, data):
if self.game().leave(current_user):
fio.emit('leave', {'username':current_user.login}, broadcast=True)
def on_ban(self, data):
if 'username' in data:
username = data['username']
if self.game().isadmin(current_user):
if 'username' is not None:
if self.game().leave(username):
fio.emit('leave', {'username':username})
def on_fixplayers(self, data):
if self.game().isadmin(current_user):
if self.game().can_start():
self.game().fix_players()
fio.emit('fixplayers', {})
def on_start(self, data):
if 'order' in data:
order = data['order']
else:
order = None
if self.game().isadmin(current_user):
if self.game().start_game(order):
fio.emit('start', {}, broadcast=True)
def game(self):
return Game.query.get(self._id)

339
db.py Normal file
View File

@ -0,0 +1,339 @@
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import relationship
import random
from belote_jeu import cards, N_PLAYERS, colors, winner, N_TURN
db = SQLAlchemy()
levels_users = {'Inactive':0, 'Simple user':1, 'Can create games':2, 'Can manage simple users': 8, 'Can manage games':9, 'Admin':10}
#######################################################################
# User class #
#######################################################################
class User(UserMixin, db.Model):
"""Model for user accounts."""
__tablename__ = 'users'
login = db.Column(db.String(200),
primary_key=True)
name = db.Column(db.String(200),
nullable=False,
unique=False)
password = db.Column(db.String(200),
primary_key=False,
unique=False,
nullable=False)
level = db.Column(db.Integer, default=1)
last_login = db.Column(db.DateTime)
def set_password(self, password):
"""Create hashed password."""
self.password = generate_password_hash(password, method='sha256')
def check_password(self, password):
"""Check hashed password."""
return check_password_hash(self.password, password)
def get_id(self):
return self.login
def __repr__(self):
return '<User {}>'.format(self.login)
#######################################################################
# Game class #
#######################################################################
class Game(db.Model):
__tablename__ = 'games'
id = db.Column(db.Integer,
primary_key=True)
name = db.Column(db.String(200))
players = relationship("Player")
turn = db.Column(db.Integer, default=-8)
atout = db.Column(db.String(1))
preneur = db.Column(db.Integer)
admin = db.Column(db.String(200), db.ForeignKey('users.login'))
cards_to_distribute = db.Column(db.String(200))
first_player = db.Column(db.Integer)
start = db.Column(db.Boolean, default=False)
last_played = db.Column(db.String(20))
played = db.Column(db.String(20))
fixplayers = db.Column(db.Boolean, default=False)
points_0 = db.Column(db.Integer, default=0)
points_1 = db.Column(db.Integer, default=0)
cumul_0 = db.Column(db.Integer, default=0)
cumul_1 = db.Column(db.Integer, default=0)
partie = db.Column(db.Integer, default=1)
def start_game(self, ordered_players):
# Ordering players
if self.fixplayers and not self.start:
i=0
if ordered_players is not None:
for op in ordered_players:
player = Player.query.filter_by(game=self.id, user=op).first()
if player is not None:
player.nr = i
i+=1
for p in self.players:
if p.nr is None or p.nr<0:
p.nr = i
i+=1
db.session.commit()
self.start = True
self.first_player = 0
self.distribute()
db.session.commit()
return True
return False
def fix_players(self):
if self.can_start():
self.fixplayers = True
db.session.commit()
def distribute(self):
___cartes = random.sample(cards, k=len(cards))
for i in range(len(self.players)):
self.players[i].cards = ','.join(___cartes[5*i:5*(i+1)])
self.played = ___cartes[5*(i+1)]
self.cards_to_distribute = ','.join(___cartes[5*(i+1)+1:])
def authorized(self, user):
for p in self.players:
if user.login == p.user:
return True
return False
def isadmin(self, user):
return user.login == self.admin
def serialize_state_anonymous(self):
r = {}
r['atout'] = self.atout
r['preneur'] = self.preneur
r['last_played'] = self.last_played.split(',') if self.last_played is not None else []
r['played'] = self.played.split(',') if self.played is not None else []
r['turn'] = self.turn
r['first_player'] = self.first_player
r['points'] = [self.points_0, self.points_1]
r['cumul'] = [self.cumul_0, self.cumul_1]
r['partie'] = self.partie
return r
def serialize_state(self, user):
r = self.serialize_state_anonymous()
r['players'] = self.get_ordered_players()
r['playersinfo'] = {}
for p in self.players:
r['playersinfo'][p.user] = User.query.get(p.user).name
r['nr'] = self.get_nr(user)
r['admin'] = self.admin
cards = self.get_player(user).cards
r['cards'] = cards.split(',') if cards is not None else []
return r
def get_ordered_players(self):
r = {}
for p in self.players:
r[p.nr] = p.user
r2 = []
for i in range(N_PLAYERS):
r2.append(r[i] if i in r else None)
return r2
def get_nr(self, user):
p = self.get_player(user)
if p is not None :
return p.nr
else:
return -1
def get_player(self, user):
if not isinstance(user, str):
user = user.login
return Player.query.filter_by(game=self.id, user=user).first()
def can_join(self, user):
if self.authorized(user):
return True
if not self.fixplayers and len(self.players) < N_PLAYERS:
return True
return False
def can_start(self):
if len(self.players)==N_PLAYERS:
return True
return False
def join(self, user):
if not self.fixplayers and not self.authorized(user):
p = Player(game=self.id, user=user.login)
db.session.add(p)
db.session.commit()
def leave(self, user):
if not self.fixplayers:
for p in self.players:
if isinstance(user, str) and user != self.admin or hasattr(user, 'login') and user.login != self.admin:
if isinstance(user, str) and user == p.user or hasattr(user, 'login') and user.login == p.user:
db.session.delete(p)
db.session.commit()
return True
return False
def get_players(self):
r = []
for p in self.players:
user = User.query.get(p.user)
r.append({'username':user.login, 'name':user.name})
return r
def tour_choix(self, atout, user):
if (self.turn + self.first_player) % N_PLAYERS == self.get_nr(user):
self.turn += 1
if atout is not None and atout in colors:
self.atout = atout
self.preneur = user.login
self.turn = 0
self.distribute_restant()
db.session.commit()
return True
return False
def distribute_restant(self):
todistribute = self.cards_to_distribute.split(',')
for pn in self.get_ordered_players():
p = self.get_player(pn)
if pn == self.preneur:
p.cards += ',' + ','.join(todistribute[:2])
p.cards += ',' + self.played
todistribute = todistribute[2:]
else:
p.cards += ',' + ','.join(todistribute[:3])
todistribute = todistribute[3:]
self.cards_to_distribute = ''
self.played = None
def tour_jeu(self, carte, user):
if (self.turn + self.first_player) % N_PLAYERS == self.get_nr(user):
winnr = -1
self.turn += 1
if self.played is None or self.played == '':
self.played = carte
else:
self.played += ',' + carte
if self.turn % N_PLAYERS == 0 :
winnr, points = winner(self.played, self.atout)
if (self.first_player + winnr)%2 == 0:
self.points_0 += points
else:
self.points_1 += points
self.last_played = self.played
if len(self.cards_to_distribute)>1:
self.cards_to_distribute += ','
self.cards_to_distribute += self.played
self.played = None
self.first_player = (self.first_player + winnr)%4
if self.turn == N_TURN*N_PLAYERS: # Dix de der, chute, belote et capote !
if (self.first_player + winnr)%2 == 0:
self.points_0 += 10
else:
self.points_1 += 10
if self.get_player(self.preneur).nr %2 ==0 and self.points_0 < self.points_1:
self.points_0 = 0
self.points_1 = 162
if self.get_player(self.preneur).nr %2 ==1 and self.points_1 < self.points_0:
self.points_0 = 162
self.points_1 = 0
if self.points_0 == 162 or self.points_1 == 162:
winequipe = 0 if self.points_0 == 162 else 1
# Capote possible.
winnr = 0
ccc = self.cards_to_distribute.split(',')
for i in range(N_TURN):
win, xxx = winner(ccc[i*N_PLAYERS:(i+1)*N_PLAYERS], self.atout)
winnr = win+winnr
if winnr % 2 != winequipe:
break
else:
if winequipe == 0:
self.points_0 += 90
else:
self.points_1 += 90
# belote possible
winnr = 0
belote =-1
ccc = self.cards_to_distribute.split(',')
for i in range(N_TURN):
cccc = ccc[i*N_PLAYERS:(i+1)*N_PLAYERS]
if 'R'+self.atout in cccc:
if belote>=0:
if (cccc.index('R'+self.atout) + winnr)%4 == belote %4:
if belote %2 ==0:
self.points_0 += 20
else:
self.points_1 += 20
else:
belote = winnr + cccc.index('R'+self.atout)
if 'D'+self.atout in cccc:
if belote>=0:
if (cccc.index('D'+self.atout) + winnr)%4 == belote %4:
if belote %2 ==0:
self.points_0 += 20
else:
self.points_1 += 20
else:
belote = winnr + cccc.index('D'+self.atout)
win, xxx = winner(cccc, self.atout)
winnr = win+winnr
# Retirer la carte du jeu !
p = self.get_player(user)
ccc = p.cards
ccc = ccc.split(',')
ccc.remove(carte)
p.cards = ','.join(ccc)
db.session.commit()
return True, winnr
return False, -1
def restart_jeu(self):
if self.turn==N_TURN*N_PLAYERS:
self.turn = -8
self.atout = None
self.preneur = None
self.last_played = None
self.played = None
self.cumul_0 += self.points_0
self.cumul_1 += self.points_1
self.points_0 = 0
self.points_1 = 0
self.partie +=1
self.first_player = 0
self.distribute()
db.session.commit()
return True
return False
#######################################################################
# Player class #
#######################################################################
class Player(db.Model):
__tablename__ = 'players'
id = db.Column(db.Integer,
primary_key=True)
game = db.Column(db.Integer, db.ForeignKey('games.id'))
user = db.Column(db.Integer, db.ForeignKey('users.login'))
cards = db.Column(db.String(40))
nr = db.Column(db.Integer, default=-1)

7
requirements.txt Normal file
View File

@ -0,0 +1,7 @@
Flask==1.1.2
Flask-Login==0.5.0
Flask-SocketIO==4.2.1
Flask-SQLAlchemy==2.4.1
SQLAlchemy==1.3.16
Werkzeug==1.0.1
eventlet==0.25.2

9
settings.exemple.cfg Normal file
View File

@ -0,0 +1,9 @@
# App configuration file
# copy this file to settings.cfg
# Generate a secure key with
# python -c "import os, base64; print(base64.b64encode(os.urandom(24)))"
SECRET_KEY = "to-be-changed"
SQLALCHEMY_DATABASE_URI = 'sqlite:///db.sqlite'
SQLALCHEMY_TRACK_MODIFICATIONS = False

73
static/belote-script.js Normal file
View File

@ -0,0 +1,73 @@
/*************************
la Belote
**************************/
var go = function(url) {
document.location.href=url;
}
$(document).ready(function() {
/*==============================
listeners à activer à ready
===============================*/
document.getElementById("username").addEventListener("click",function() {apparaitDisparaitMenu('username-items','username-fleche')});
var options = document.getElementById("options");
if(options){
options.addEventListener("click",function() {apparaitDisparaitAccordeon('options-valeur','options-fleche')});
}
var derpli = document.getElementById("dernier-pli")
if(derpli){
derpli.addEventListener("click",function() {apparaitDisparaitAccordeon('dernier-pli-valeur','dernier-pli-fleche')});
}
/*==============================
fonctions
===============================*/
/*
fonction apparaitDisparaitUserAttr
apparition/disparition d'un menu
*/
function apparaitDisparaitMenu (idMenu,idFleche) {
var element = document.getElementById(idMenu);
if(element.style.visibility == "hidden" || element.style.visibility == "") {
document.getElementById(idMenu).style.visibility = "visible";
}
else {
document.getElementById(idMenu).style.visibility = "hidden";
};
var elementFleche = document.getElementById(idFleche);
var splitElement = elementFleche.src.split('/');
var lastElement = splitElement[splitElement.length-1];
if(lastElement == "fleche-bas.gif") {
document.getElementById(idFleche).src = "/static/fleche-haut.gif";
}
else {
document.getElementById(idFleche).src = "/static/fleche-bas.gif";
}
return(0);
}
function apparaitDisparaitAccordeon (idZone,idFleche) {
var element = document.getElementById(idZone);
if(element.style.display == "none" || element.style.display == "") {
document.getElementById(idZone).style.display = "block";
}
else {
document.getElementById(idZone).style.display = "none";
};
var elementFleche = document.getElementById(idFleche);
var splitElement = elementFleche.src.split('/');
var lastElement = splitElement[splitElement.length-1];
if(lastElement == "fleche-bas.gif") {
document.getElementById(idFleche).src = "/static/fleche-haut.gif";
}
else {
document.getElementById(idFleche).src = "/static/fleche-bas.gif";
}
return(0);
}
/* fin de $(document).ready() */
});

391
static/belote.css Normal file
View File

@ -0,0 +1,391 @@
/* =========================
la Belote
========================= */
/****************/
/* Reset */
/****************/
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,font,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{margin:0;padding:0;border:0;vertical-align:baseline;background:transparent}
body{line-height:1}
ol,ul{list-style:none}
blockquote,q{quotes:none}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}
:focus{outline:0}
ins{text-decoration:none}
del{text-decoration:line-through}
table{border-collapse:collapse;border-spacing:0}
/****************/
/* généralités */
/****************/
body {
margin: 0px;
background-color: #13995E;
}
div.clear {
clear: both;
}
div.clearfix-head::after {
content:"";
clear: both;
display: table;
border: 1px solid black;
width: 100%;
}
div.clearfix::after{
content:"";
clear: both;
display: table;
}
div, p, li, a, th, td {
font-family: 'Segoe UI', Arial, Helvetica, sans-serif;
font-size: 15px;
line-height: 17px;
color: #092E1E;
}
/****************/
/* conteneur */
/****************/
#conteneur {
margin-top: 8px;
margin-right: auto;
margin-bottom: 0px;
margin-left: auto;
height: 600px;
width: 1200px;
/* border: 1px solid #092E1E; */
}
/****************/
/* entete */
/****************/
#entete {
position: relative;
height: 66px;
margin: 2px;
/*
border: 1px solid #092E1E;
*/
}
#logo {
margin: 2px 10px 2px 2px;
float: left;
}
#logo img {
height: 60px;
width: 60px;
display: block;
margin-left: auto;
margin-right: auto;
}
#titre {
width: 55%;
line-height: 60px;
margin: 2px 15px 2px 2px;
text-align: center;
font-size: 140%;
font-weight: bold;
float: left;
}
#username {
width: 20%;
height: 60px;
line-height: 60px;
margin: 2px 15px 2px 30px;
float: right;
font-size: 140%;
font-weight: bold;
text-align: right;
}
#username-fleche {
height: 14px;
width: 14px;
cursor: pointer;
padding-left: 10px;
vertical-align: baseline;
}
#username-items {
position: absolute;
top: 68px;
right: 0px;
width: 180px;
visibility: hidden;
margin-top: 2px ;
padding: 5px;
border: 1px solid #092E1E;
overflow: hidden;
z-index: 100;
background-color: #b7a86b;
}
.menu-item:hover {
cursor: pointer;
background-color: #15A160;
}
/****************/
/* zone-de-contenu */
/****************/
#zone-de-contenu {
width:50%;
margin:auto;
}
#zone-de-contenu>div {
margin:auto;
}
.tablelist {
width:100%;
}
/****************/
/* zone-de-jeu */
/****************/
#zone-de-jeu {
height: 438px;
margin: 2px;
/*
border: 1px solid #092E1E;
*/
}
#plateau {
height: 320px;
width: 740px;
position: relative;
margin: 3px;
}
#joueur-0 {
position: absolute;
top: 55px;
left: 190px;
text-align: right;
}
#joueur-1 {
position: absolute;
top: 145px;
left: 80px;
text-align: right;
}
#joueur-2 {
position: absolute;
top: 245px;
left: 190px;
text-align: right;
}
#joueur-3 {
position: absolute;
top: 145px;
left: 500px;
}
#jeu-0 {
position: absolute;
top: 30px;
left: 300px;
}
#jeu-1 {
position: absolute;
top: 120px;
left: 190px;
}
#jeu-2 {
position: absolute;
top: 220px;
left: 300px;
}
#jeu-3 {
position: absolute;
top: 120px;
left: 420px;
}
#suivant {
position: absolute;
bottom: 15px;
right: 90px;
}
#suivant input {
width: 80px;
}
#ov {
height: 310px;
width: 390px;
padding: 5px 25px 5px 25px;
position: relative;
top: -322px;
left: 746px;
margin: 2px;
border: 1px solid #092E1E;
}
#table-atout {
width: 98%;
}
#table-atout td {
padding: 4px;
text-align: center;
vertical-align: middle;
}
#atout {
margin: 0px auto 0px auto;
}
#choix {
height: 160px;
margin-top: 25px;
padding: 6px;
border: 1px solid #092E1E;
overflow-x: hidden;
overflow-y: auto;
}
#options-fleche {
width: 11px;
height: 11px;
float: right;
cursor: pointer;
vertical-align: baseline;
}
#options-valeur {
display: none;
}
#dernier-pli-fleche {
width: 11px;
height: 11px;
float: right;
cursor: pointer;
vertical-align: baseline;
}
#dernier-pli-valeur {
display: none;
}
#dernier-pli {
margin-top: 10px;
}
#dernier-pli-valeur {
padding-left: 50px;
}
#jeu-en-main {
height: 76px;
width: 642px;
position: relative;
top: -320px;
left: 0px;
margin: 2px;
padding: 15px 58px 15px 40px;
border: 1px solid #092E1E;
}
#a-vous {
float: left;
width: 90px;
height: 79px;
padding-top: 20px;
text-align: center;
margin-right: 20px;
visibility: hidden;
}
#points {
height: 98px;
width: 390px;
position: relative;
top: -430px;
left: 746px;
margin: 2px;
border: 1px solid #092E1E;
padding: 4px 25px 4px 25px;
}
#points p {
text-align: center;
margin: 5px 0px 5px 0px;
}
#points table {
width: 98%;
border-collapse: collapse;
border: 1px solid #092E1E;
}
#points table td {
padding: 0px 2px 0px 2px;
text-align: center;
border: 1px solid #092E1E;
}
.joueur {
width: 90px;
height: 18px;
overflow: hidden;
text-decoration: underscore;
}
.carte {
height: 77px;
width: 50px;
/* carte : hauteur = 1,54 * largueur; */
border: 1px solid #092E1E;
background-color: #117F4A;
}
.carte-en-main, .carte-dernier-pli {
float: left;
margin-right: 12px;
}
.item-choix {
padding: 4px;
border: 1px solid #092E1E;
}
.item-choix-valeur {
padding: 4px;
border-right: 1px solid #092E1E;
border-bottom: 1px solid #092E1E;
border-left: 1px solid #092E1E;
}
/************************/
/* zone-de-conversation */
/************************/
#zone-de-conversation {
height: 84px;
margin: 2px;
/*
border: 1px solid #092E1E;
*/
position: relative;
}
#chat {
width: 61%;
height: 87%;
float: left;
background-color: #C4E8D7;
margin: 2px 15px 2px 2px;
border-top: 2px solid #888;
border-right: 2px solid #DDD;
border-bottom: 2px solid #DDD;
border-left: 2px solid #888;
position: absolute;
overflow-x: hidden;
overflow-y: auto;
}
#message {
width: 34%;
float: right;
margin: 1px 15px 2px 2px;
position: absolute;
right: 2px;
top: 3px;
}
#message p {
padding-bottom: 4px;
}
#texte-msg {
width:80%;
background-color: #C4E8D7;
margin-right: 5px;
}
button#add{
min-width:180px;
margin:auto;
padding:10px;
background: #b7a86b;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
-khtml-border-radius: 10px;
border-radius: 10px;
display:block;
}
thead{
font-weight: bold;
}
fieldset.fieldset {
border:1px solid green;
padding:30px;
margin:20px;
}

BIN
static/cards/0C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/cards/0F.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/cards/0P.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/cards/0T.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
static/cards/7C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
static/cards/7F.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
static/cards/7P.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
static/cards/7T.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
static/cards/8C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
static/cards/8F.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

BIN
static/cards/8P.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

BIN
static/cards/8T.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/cards/9C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/cards/9F.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
static/cards/9P.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
static/cards/9T.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/cards/AC.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
static/cards/AF.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
static/cards/AP.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
static/cards/AT.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
static/cards/C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
static/cards/DC.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
static/cards/DF.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
static/cards/DP.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
static/cards/DT.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
static/cards/F.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
static/cards/P.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
static/cards/RC.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
static/cards/RF.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
static/cards/RP.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
static/cards/RT.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
static/cards/T.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
static/cards/VC.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
static/cards/VF.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
static/cards/VP.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
static/cards/VT.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
static/croix.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

BIN
static/edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 B

BIN
static/fleche-bas.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 B

BIN
static/fleche-haut.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 B

262
static/game_script.js Normal file
View File

@ -0,0 +1,262 @@
var socket = null;
var sendtext = function(){
var text = $('#texte-msg').val();
if (text.length > 1){
$('#texte-msg').val('');
socket.emit('text', {'text':text});
}
};
var choixcouleur = function(coul){
socket.emit('choose_color', {'atout':coul});
}
var jouer = function(carte, no){
socket.emit('play', {'card':carte});
}
var restart_jeu = function(){
socket.emit('restart', {});
}
$.fn.pressEnter = function (fnc) {
return this.each(function () {
$(this).keypress(function (ev) {
var keycode = (ev.keyCode ? ev.keyCode : ev.which);
if (keycode == '13') {
fnc.call(this, ev);
}
})
})
}
var set_points = function(){
$('#nous-tot').html( jeu['cumul'][jeu['nr']%2] );
$('#nous-part').html(jeu['points'][jeu['nr']%2] );
$('#eux-tot').html( jeu['cumul'][(jeu['nr']+1)%2] );
$('#eux-part').html( jeu['points'][(jeu['nr']+1)%2]);
$('#nopart').html('Partie '+jeu['partie']);
}
var set_atout = function(){
if(jeu['atout'] == null || jeu['atout']==''){
var coulprop = jeu['played'][0];
$('#atout').html(`<img src="/static/cards/${coulprop}.png" width=100% />`);
if (jeu['turn']<-4 && (jeu['turn']+jeu['first_player']+8)%4 == jeu['nr']){
$('#prispar').html(`<input type="submit" onclick="javascript:choixcouleur('${coulprop[1]}')" value="Prendre" />`);
$('#preneur').html(`<input type="submit" onclick="javascript:choixcouleur(null)" value="Passer" />`);
}else if((jeu['turn']+jeu['first_player']+8)%4 == jeu['nr']){
$('#prispar').html(`<input type="submit" onclick="javascript:choixcouleur('P')" value="Pique" />
<input type="submit" onclick="javascript:choixcouleur('C')" value="Coeur" />
<br />
<input type="submit" onclick="javascript:choixcouleur('T')" value="Trèfle" />
<input type="submit" onclick="javascript:choixcouleur('F')" value="Carreau" />
`);
$('#preneur').html(`<input type="submit" onclick="javascript:choixcouleur(null)" value="Passer" />`);
}else{
$('#prispar').html(`Pris par`);
$('#preneur').html(`----`);
}
}else{
$('#atout').html(`<img src="/static/cards/${jeu['atout']}.png" width=100% />`);
$('#prispar').html(`Pris par`);
$('#preneur').html(`${jeu['playersinfo'][jeu['preneur']]}`);
}
}
jeu['monjeu'] = Array.from(jeu['cards']);
var order = ['A', '0', 'R', 'D', 'V', '9', '8', '7'];
var order_atout = ['V', '9', 'A', '0', 'R', 'D', '8', '7'];
var set_jeu = function(){
var order_colors = ['P', 'C', 'T', 'F'];
if (jeu['atout'] == 'C'){
order_colors = ['C', 'T', 'F', 'P'];
}else if (jeu['atout'] == 'T'){
order_colors = ['T', 'C', 'P', 'F'];
}else if (jeu['atout'] == 'F'){
order_colors = ['F', 'T', 'C', 'P'];
}
jeu['monjeu'].sort(function(a,b){
if (a[1]==jeu['atout']){
x = order_atout.indexOf(a[0]);
}else{
x = order.indexOf(a[0]);
}
if (b[1]==jeu['atout']){
y = order_atout.indexOf(b[0]);
}else{
y = order.indexOf(b[0]);
}
return x+15*order_colors.indexOf(a[1]) > y+15*order_colors.indexOf(b[1]) ? 1 : -1;
});
console.log(jeu['monjeu']);
for(i=0;i<jeu['monjeu'].length;i++){
if (jeu['monjeu'][i]==null){
$('#carte-'+i).html('');
}else{
$('#carte-'+i).html(`<a href="javascript:jouer('${jeu['monjeu'][i]}', ${i})"><img src="/static/cards/${jeu['monjeu'][i]}.png" width=100% /></a>`);
}
}
}
var set_cartes = function(){
first_player = jeu['first_player']
if (jeu['turn']%4==0 && 'lastfirstplayer' in jeu){
first_player = jeu['lastfirstplayer']
}
if (jeu['preneur'] == null){
$('#jeu-1').html('');
$('#jeu-2').html('');
$('#jeu-3').html('');
$('#jeu-4').html('');
}else{
for(i=first_player;i<first_player+4;i++){
j = i-first_player
k = i%4
if (jeu['played'].length > j && jeu['played'][j]!= ''){
$('#jeu-'+ k).html(`<img src="/static/cards/${jeu['played'][j]}.png" width=100% />`);
}else{
$('#jeu-'+ k).html('');
}
}
}
}
var set_derpli = function(){
if(jeu['last_played'].length >0){
for(i=0;i<4;i++){
$('#der-pli-'+i).html(`<img src="/static/cards/${jeu['last_played'][i]}.png" width=100% />`);
}
}
}
var set_players = function(){
for(i=0;i<4;i++){
$('#joueur-'+i).html(`${jeu['playersinfo'][jeu['players'][i]]}`);
}
}
var set_current_player = function(){
for(i=0;i<4;i++){
$('#joueur-'+i).css('font-weight', 'normal');
}
i = (jeu['turn']+jeu['first_player']+8)%4;
$('#joueur-'+i).css('font-weight', 'bold');
if((jeu['turn']+jeu['first_player']+8)%4 == jeu['nr']){
$('#a-vous').css('visibility', 'visible');
}else{
$('#a-vous').css('visibility', 'hidden');
}
if(jeu['turn']==32 && jeu['admin']==jeu['players'][jeu['nr']]){
$('#suivant').show();
}
}
$(document).ready(function() {
socket = io(window.location.pathname);
$('#suivant').hide()
set_points();
set_atout();
set_jeu();
set_cartes();
set_derpli();
set_players();
set_current_player();
for (e in jeu){
$('#jeu').append(`${e} : ${jeu[e]} <br />`)
}
if((jeu['turn']+jeu['first_player'] + 8)%4 == jeu['nr']){
$('#a-vous').css('visibility', 'visible');
}
socket.on('connect', function() {
socket.emit('join', {data: 'I\'m connected!'});
});
socket.on('text', function(data) {
textr = data['text'];
name = data['name'];
$('#chat').prepend(`<b>${name}</b> : ${textr}<br />`);
});
socket.on('restart', function(data) {
for(d in data){
jeu[d] = data[d]
}
set_points();
set_atout();
set_cartes();
set_derpli();
set_players();
set_current_player();
socket.emit('monjeu', {});
});
socket.on('choose_color', function(data){
console.log(data)
jeu['turn'] = data['turn']
jeu['preneur'] = data['preneur']
jeu['atout'] = data['atout']
set_atout();
set_current_player();
if(jeu['turn']>=0){
socket.emit('monjeu', {});
}
});
socket.on('monjeu', function(data){
jeu['cards'] = data['cards'];
jeu['monjeu'] = Array.from(data['cards']);
set_jeu();
});
socket.on('play', function(data){
console.log(data)
jeu['turn'] = data['turn'];
jeu['lastfirstplayer'] = jeu['first_player']
jeu['first_player'] = data['first_player'];
jeu['last_played'] = data['last_played'];
jeu['points'] = data['points'];
if(data['played'] == null || data['played'].length == 0){
jeu['played'] = data['last_played'];
console.log(jeu['played'])
set_cartes();
set_derpli();
set_points();
}
else{
jeu['played'] = data['played'];
console.log(jeu['played'])
set_cartes();
set_points();
}
var first_player = jeu['first_player'];
if (jeu['turn']%4==0 && 'lastfirstplayer' in jeu){
first_player = jeu['lastfirstplayer']
}
if((jeu['turn']+first_player+7)%4 == jeu['nr']){
console.log("Je viens de jouer")
i = jeu['monjeu'].indexOf(jeu['played'][jeu['played'].length-1]);
if (jeu['played'].length>0 && i >=0){
console.log(i)
jeu['monjeu'][i] = null;
$('#carte-'+i).html('')
}
}
set_current_player();
});
$('#texte-msg').pressEnter(function(e){
sendtext();
});
});

BIN
static/logo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 849 B

BIN
static/ok.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

BIN
static/plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,28 @@
{% extends "base.html" %}
{% block title %} Créer jeu {% endblock %}
{% block titre %} Créer un jeu {% endblock %}
{% block contenu %}
<div id="zone-de-contenu">
<div style="width:100%;margin:20px;"> </div>
<fieldset class="fieldset"><legend style="font-size:25px;">Nom du jeu</legend>
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ message }}
</div>
{% endfor %}
<form action="" method="POST" accept-charset="utf-8">
Nom du jeu : <input type="text" name="name" id="name" placeholder="Nom du jeu"/><br />
Maitre du jeu : <input type="text" name="admin" placeholder="Login du maitre du jeu" /><br />
<input id="submit" type="submit" value="Créer">
</form>
</fieldset>
</div>
{% endblock %}

View File

@ -0,0 +1,27 @@
{% extends "base.html" %}
{% block title %} Administration {% endblock %}
{% block titre %}Administration : jeux {% endblock %}
{% block contenu %}
<div id="zone-de-contenu">
<fieldset class="fieldset" ><legend style="font-size:25px;">Ajouter un utilisateur</legend>
<button id="add" onclick="javascript:go('/admin/games/new')">
<img src="/static/plus.png" style="float:left;" alt=''/><div style="float:right; margin:5px;">Nouveau jeu</div>
</button>
</fieldset>
<fieldset class="fieldset"><legend style="font-size:25px;">Liste des jeux</legend>
<table width=100%>
<thead>
<tr><td>Id</td><td>Name</td><td>Partie</td><td>Joueurs</td><td>Status</td><td>Actions</td></tr>
</thead>
{% for g in games %}
<tr><td>{{ g.id }}</td><td><a href="/game/{{g.id}}">{{ g.name }}</a></td><td>{{ g.partie }}</td><td>{% for p in g.players %}{{ p.user }} {% endfor %}</td>
<td>{% if g.start %} Started {% elif g.fixplayers %}Players set{% else %}Join{% endif %}</td>
<td><a href="/game/{{g.id}}/see">Voir</a>&nbsp;<a href="/admin/games/{{ g.id }}/del"><img src="/static/croix.png" alt="Del" /></a></td></tr>
{% endfor %}
</table>
</fieldset>
</div>
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block title %} Jeux {% endblock %}
{% block titre %} Suppression utilisateur {{ useredit.name }} {% endblock %}
{% block contenu %}
<div id="zone-de-contenu">
<fieldset class="fieldset"><legend style="font-size:25px;">Confirmation</legend>
<p>Sur de vouloir supprimer l'utilisateur <b>{{ useredit.name }} ({{useredit.login}})</b> ? </p>
<br />
<table width=100% style="text-align:center">
<tr><td><a href="?confirm=1">Oui</a></td><td> <a href="/admin/users">Non</a></td></tr>
</table>
</fieldset>
</div>
{% endblock %}

View File

@ -0,0 +1,40 @@
{% extends "base.html" %}
{% block title %} Administration {% endblock %}
{% block titre %} Administration : Éditer un utilisateur {% endblock %}
{% block contenu %}
<div id="zone-de-contenu">
<form method="POST" action="">
<div style="width:100%;margin:20px;"> </div>
<fieldset class="fieldset"><legend style="font-size:25px;">Utilisateur</legend>
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ message }}
</div>
{% endfor %}
<table width=100%>
<tr><td>Nom d'utilisateur : </td><td> <input type="text" name="username" placeholder="jean.dupont" {% if useredit is not none %} value="{{useredit.login}}" readonly="readonly"{% endif %} /></td></tr>
<tr><td>Nom complet : </td><td> <input type="text" name="name" placeholder="Mr Jean Dupont" {% if useredit is not none %} value="{{useredit.name}}" {% endif %} /></td></tr>
<tr><td> Mot de passe : </td><td> <input type="password" name="password" placeholder="Mot de passe"/></td></tr>
<tr><td> Droits : </td><td> <select name="level">
{% for e in levels_users %}
{% if useredit is none %}
<option value="{{ levels_users[e] }}" {% if e == 'Simple user' %}selected{% endif %}>{{ e }}</option>
{% else %}
<option value="{{ levels_users[e] }}" {% if useredit.level == levels_users[e] %}selected{% endif %}>{{ e }}</option>
{% endif %}
{% endfor %}
</select>
</td></tr>
<tr><td>&nbsp;</td> <td><input id="submit" type="submit" value="&nbsp;Entrer&nbsp;"></td></tr>
</table>
</fieldset>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,25 @@
{% extends "base.html" %}
{% block title %} Administration {% endblock %}
{% block titre %}Administration : utilisateurs {% endblock %}
{% block contenu %}
<div id="zone-de-contenu">
<fieldset class="fieldset" ><legend style="font-size:25px;">Ajouter un utilisateur</legend>
<button id="add" onclick="javascript:go('/admin/users/new')">
<img src="/static/plus.png" style="float:left;" alt=''/><div style="float:right; margin:5px;">Nouvel utilisateur</div>
</button>
</fieldset>
<fieldset class="fieldset"><legend style="font-size:25px;">Liste des utilisateurs</legend>
<table width=100%>
<thead>
<tr><td>Login</td><td>Name</td><td>Level</td><td>Last login</td><td>Actions</td></tr>
</thead>
{% for u in users %}
<tr><td>{{ u.login }}</td><td>{{ u.name }}</td><td>{{ u.level }}</td><td>{{ u.last_login }}</td><td><a href="/admin/users/{{ u.login }}"><img src="/static/edit.png" alt="Edit" /></a>&nbsp;<a href="/admin/users/{{ u.login }}/del"><img src="/static/croix.png" alt="Del" /></a></td></tr>
{% endfor %}
</table>
</fieldset>
</div>
{% endblock %}

52
templates/base.html Normal file
View File

@ -0,0 +1,52 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
{% include ['head-perso.html', 'head.html'] %}
{% block head %}
<title>{% block title %}{% endblock %} - Belote</title>
{% endblock %}
</head>
<body>
<div id="conteneur">
<!-- ENTETE -->
<div id="entete" class="clearfix-head">
<div id="logo">
<img src="/static/logo.gif" />
</div><!-- logo -->
<div id="titre">
{% block titre %} {% endblock %}
</div><!-- titre -->
<div id="username">
{% if not user == None %}
{{ user.name }}
{% else %}
S'identifier
{% endif %}
<img id="username-fleche" src="/static/fleche-bas.gif"/>
</div><!-- usernamev -->
<div id="username-items">
{% if not user == None %}
<div id="username-item1" class="menu-item" onclick="javascript:go('/games')">Liste des jeux</div>
<div id="username-item2" class="menu-item" onclick="javascript:go('/password')">Changer mon mot de passe</div>
<div id="username-item3" class="menu-item" onclick="javascript:go('/logout')">Se déconnecter</div>
{% if user.level > 9 %}
<div id="username-item4" class="menu-item" onclick="javascript:go('/admin/games')">Admin des jeux</div>
<div id="username-item5" class="menu-item" onclick="javascript:go('/admin/users')">Admin des utilisateurs</div>
{% endif %}
{% else %}
<div id="username-item1" class="menu-item" onclick="javascript:go('/login')">S'identifier</div>
{% endif %}
</div><!-- username-items-->
</div><!-- entete -->
<!-- CONTENU -->
{% block contenu %}
{% endblock %}
<!-- / CONTENU -->
</div>
{% block script %} {% endblock %}
</body>
</html>

44
templates/belote.html Normal file
View File

@ -0,0 +1,44 @@
<!--
La Belote
-->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta http-equiv="cache-control" content="no-cache" />
<title>Belote</title>
<!-- <link rel='stylesheet' type='text/css' href='./belote-reset.css' />-->
<link rel='stylesheet' type='text/css' href='./belote-presentation.css' />
<script src="./jquery-3.5.0.js"></script>
<script src="./belote-script.js"></script>
</head>
<!----------------------
BODY
----------------------->
<body>
<div id="conteneur">
<!----------------------
ENTETE
----------------------->
<div id="entete" class="clearfix-head">
<div id="logo">
<img src="logo.gif" />
</div><!-- logo -->
<div id="titre">
TITRE
</div><!-- titre -->
<div id="username">
username
<img id="username-fleche" src="./fleche-bas.gif"/>
</div><!-- usernamev -->
<div id="username-items">
<div id="username-item1" class="menu-item">- item 1</div>
<div id="username-item2" class="menu-item">- item 2</div>
</div><!-- username-items-->
</div><!-- entete -->
<!----------------------
ZONE-DE-JEU
----------------------->
/div><!-- conteneur -->
</body>
</html>

21
templates/game.html Normal file
View File

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block title %} Jeu {% endblock %}
{% block titre %} Jeu {{ game.name }} {% endblock %}
{% block head %}
{% include ['scripts-perso.html', 'scripts.html'] %}
{% endblock %}
{% block contenu %}
{% include "game_zone_jeu.html" %}
{% endblock %}
{% block script %}
<script type="text/javascript" charset="utf-8">
var None = null;
var jeu = {{ game.serialize_state(user)|safe }};
</script>
<script src="/static/game_script.js" charset="utf-8"></script>
{% endblock %}

21
templates/game_add.html Normal file
View File

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block title %} Créer jeu {% endblock %}
{% block titre %} Créer un jeu {% endblock %}
{% block contenu %}
<div id="zone-de-contenu">
<div style="width:100%;margin:20px;"> </div>
<fieldset class="fieldset"><legend style="font-size:25px;">Nom du jeu</legend>
<form action="" method="POST" accept-charset="utf-8">
Nom du jeu : <input type="text" name="name" id="name" />
<input id="submit" type="submit" value="Créer">
</form>
</fieldset>
</div>
{% endblock %}

17
templates/game_del.html Normal file
View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block title %} Jeux {% endblock %}
{% block titre %} Suppression jeu {{game.name }} {% endblock %}
{% block contenu %}
<div id="zone-de-contenu">
<fieldset class="fieldset"><legend style="font-size:25px;">Confirmation</legend>
<p>Sur de vouloir supprimer le jeu {{ game.name }} ? </p>
<br />
<table width=100% style="text-align:center">
<tr><td><a href="?confirm=1">Oui</a></td><td> <a href="/games">Non</a></td></tr>
</table>
</fieldset>
</div>
{% endblock %}

166
templates/game_join.html Normal file
View File

@ -0,0 +1,166 @@
{% extends "base.html" %}
{% block title %} Jeu {% endblock %}
{% block titre %} Jeu {{game.name}} {% endblock %}
{% block head %}
{% include ['scripts-perso.html', 'scripts.html'] %}
{% endblock %}
{% block contenu %}
<div id="zone-de-contenu">
<div style="width:100%;margin:20px;"> </div>
<fieldset class="fieldset"><legend style="font-size:25px;">Liste des joueurs</legend>
<ul id="players">
{% for p in game.get_players() %}
{% if p['username'] == admin %}
<li id="player_{{ p['username'] }}"> {{ p['name'] }} ({{ p['username'] }}) (admin)</li>
{% else %}
{% if p['username'] == user.login %}
<li id="player_{{ p['username'] }}"> {{ p['name'] }} ({{ p['username'] }}) <a href="javascript:leave()">(quitter le jeu)</a></li>
{% elif user.login == admin %}
<li id="player_{{ p['username'] }}"> {{ p['name'] }} ({{ p['username'] }}) <a href="javascript:ban('{{ p['username'] }}')">(bannir)</a></li>
{% else %}
<li id="player_{{ p['username'] }}"> {{ p['name'] }} ({{ p['username'] }})</li>
{% endif %}
{% endif %}
{% endfor %}
</ul>
</fieldset>
{% if user.login == admin %}
<fieldset class="fieldset"><legend style="font-size:25px;">Gestion des participants</legend>
<form action="javascript:addplayer()">
Ajouter par nom d'utilisateur :
<input type="text" id="addplayer" />
<input type="submit" value="Ajouter">
</form>
<br />
Vous pouvez aussi donner l'adresse du jeu (lire dans la barre d'adresse).
</fieldset>
<fieldset class="fieldset" id="gostart"><legend style="font-size:25px;">Démarrer le jeu</legend>
<a href="javascript:gostart()">Commencer</a>
</fieldset>
{% else %}
<fieldset class="fieldset"><legend style="font-size:25px;">Info</legend>
<p>Veuillez attendre que le maitre du jeu lance la partie...</p>
<p>Rechargez la page si cela traine trop.</p>
</fieldset>
{% endif %}
{% endblock %}
{% block script %}
<script type="text/javascript" charset="utf-8">
var players = [
{% for p in game.get_players() %}
'{{ p['username'] }}',
{% endfor %}
];
{% if game.fixplayers %}
var fix = true;
{% else %}
var fix = false;
{% endif %}
{% if user.login == admin %}
if (players.length < 4){
$('#gostart').hide();
}
else{
$('#add_player').hide();
}
{% endif %}
var socket = io(window.location.pathname);
socket.on('connect', function() {
socket.emit('join', {data: 'I\'m connected!'});
});
socket.on('join', function(data){
console.log('Join received', data['username'], data['name']);
add_u(data['username'], data['name']);
{% if user.login == admin %}
if (players.length == 4){
$('#gostart').show();
$('#add_player').hide();
}
{% endif %}
});
var add_u = function(username, name){
if(players.indexOf(username)<0){
players.push(username);
html = `<li id="player_${username}"> ${name} (${username})`
if(username == '{{ admin }}'){html+= ` (admin)`;}
else if(username == '{{ user.login }}'){html+= ` <a href="javascript:leave()">(quitter le jeu)</a>`;}
{% if user.login == admin -%}
else{html+= ` <a href="javascript:ban('${username}')">(bannir)</a>`;}
{%- endif %}
html += `</li>`
$('#players').append(html)
fix_f();
}
}
socket.on('leave', function(data){
console.log('Leave received', data['username']);
username = data['username'];
players.splice(players.indexOf(username), 1);
$('#player_' + username).remove();
{% if user.login == admin %}
$('#gostart').hide();
$('#add_player').show();
{% endif %}
});
socket.on('fixplayers', function(data){
fix = True;
fix_f();
});
var fix_f=function(){
if(fix){
$('#players li a').remove();
{% if user.login == admin %}
$('#add_player').hide();
{% endif %}
}
}
fix_f();
socket.on('start', function(data){
document.location.href="/game/{{ game.id }}";
});
var leave = function(){
socket.emit('leave', {});
document.location.href ="/games";
}
{% if user.login == admin %}
var ban = function(username){
socket.emit('ban', {'username': username});
}
var gostart = function(){
socket.emit('fixplayers', {});
document.location.href="/game/{{ game.id }}/start";
}
var addplayer = function(){
username = $('#addplayer').val();
if(username !=""){
$('#addplayer').val('');
socket.emit('add_player',{'username':username})
}
}
{% endif %}
</script>
{% endblock %}

88
templates/game_start.html Normal file
View File

@ -0,0 +1,88 @@
{% extends "base.html" %}
{% block title %} Jeu {% endblock %}
{% block titre %} Jeu {{ game.name }} {% endblock %}
{% block head %}
{% include ['scripts-perso.html', 'scripts.html'] %}
{% endblock %}
{% block contenu %}
<div id="zone-de-contenu">
<fieldset class="fieldset"><legend style="font-size:25px;">Liste des joueurs</legend>
<p>Faites les équipes :</p>
<ul id="players">
</ul>
</fieldset>
<fieldset class="fieldset"><legend style="font-size:25px;">Commencer</legend>
<a href="javascript:gostart()">Commencer</a>
</fieldset>
</div>
{% endblock %}
{% block script %}
<script type="text/javascript" charset="utf-8">
var players_order = [
{% for p in game.get_players() %}
'{{ p['username'] }}',
{% endfor %}
];
var players = {
{% for p in game.get_players() %}
'{{ p['username'] }}': '{{ p['name'] }}',
{% endfor %}
};
var put_players = function(){
var html = '';
for(i=0;i<players_order.length;i++){
html+= `<li> <div style="float:right"> <a href="javascript:up(${i})">Monter</a> &nbsp; <a href="javascript:down(${i})">Descendre</a></div> ${players[players_order[i]]} ( ${players_order[i]} ) <div style="clear:both"></div></li>`;
}
$('#players').html(html);
}
put_players();
var up=function(i){
t = players_order[i];
if(i==0){
n=players_order.length-1;
}else{
n = i-1;
}
players_order[i] = players_order[n]
players_order[n] = t
put_players();
}
var down = function(i){
t = players_order[i];
if(i==players_order.length-1){
n=0;
}else{
n = i+1;
}
players_order[i] = players_order[n]
players_order[n] = t
put_players();
}
var socket = io('/game/{{ game.id }}/join');
socket.on('connect', function() {
});
socket.on('start', function(data){
document.location.href="/game/{{ game.id }}";
});
var gostart = function(){
socket.emit('start', {'order':players_order});
document.location.href="/game/{{ game.id }}";
}
</script>
{% endblock %}

View File

@ -0,0 +1,95 @@
<div id="zone-de-jeu">
<div id="plateau">
<div id="joueur-0" class="joueur">joueur 0</div><!-- joueur-0 -->
<div id="joueur-1" class="joueur">joueur 1</div><!-- joueur-1 -->
<div id="joueur-2" class="joueur">joueur 2</div><!-- joueur-2 -->
<div id="joueur-3" class="joueur">joueur 3</div><!-- joueur-3 -->
<div id="jeu-0" class="carte"></div><!-- jeu-0 -->
<div id="jeu-1" class="carte"></div><!-- jeu-1 -->
<div id="jeu-2" class="carte"></div><!-- jeu-2 -->
<div id="jeu-3" class="carte"></div><!-- jeu-3 -->
<form id="suivant">
<input type="submit" name="suivant" value="Recommencer" onclick="javascript:restart_jeu()">
</form><!-- suivant -->
</div><!-- plateau -->
<div id="ov">
<table id="table-atout">
<tr>
<td>Atout</td>
<td><div id="atout" class="carte"></div></td>
</tr>
<tr>
<td id="prispar">Pris par </td>
<td id="preneur">----</td>
</tr>
</table>
<div id="choix">
<div id="dernier-pli">
<p class="item-choix">Dernier pli <img id="dernier-pli-fleche" src="/static/fleche-bas.gif" /></p>
<div id="dernier-pli-valeur" class="item-choix-valeur clearfix">
<div id="der-pli-0" class="carte carte-dernier-pli"></div>
<div id="der-pli-1" class="carte carte-dernier-pli"></div>
<div id="der-pli-2" class="carte carte-dernier-pli"></div>
<div id="der-pli-3" class="carte carte-dernier-pli"></div>
</div>
</div><!-- dernier-pli -->
<div id="options">
<p class="item-choix">Options <img id="options-fleche" src="/static/fleche-bas.gif" /></p>
<!--<div id="options-valeur" class="item-choix-valeur">Texte</div>-->
</div><!-- options -->
</div><!-- choix -->
</div><!-- ov -->
<div id="jeu-en-main" class="clearfix">
<div id="a-vous">A vous de jouer !
</div>
<div id="carte-0" class="carte carte-en-main">
</div>
<div id="carte-1" class="carte carte-en-main">
</div>
<div id="carte-2" class="carte carte-en-main">
</div>
<div id="carte-3" class="carte carte-en-main">
</div>
<div id="carte-4" class="carte carte-en-main">
</div>
<div id="carte-5" class="carte carte-en-main">
</div>
<div id="carte-6" class="carte carte-en-main">
</div>
<div id="carte-7" class="carte carte-en-main">
</div>
</div><!-- jeu-en-main -->
<div id="points">
<p id="entete-points">Points
</p>
<table>
<tr>
<td></td>
<td>Total</td>
<td id="nopart">Partie</td>
</tr>
<tr>
<td>Nous</td>
<td id="nous-tot">0</td>
<td id="nous-part">0</td>
</tr>
<tr>
<td>Eux</td>
<td id="eux-tot">0</td>
<td id="eux-part">0</td>
</tr>
</table>
</div><!-- points -->
</div><!-- zone-de-jeu -->
<!------------------------
ZONE-DE-CONVERSATION
------------------------->
<div id="zone-de-conversation">
<div id="chat">
</div><!-- chat -->
<div id="message">
<p>Message</p>
<input id="texte-msg" type="text" name="msg">
<input type="submit" name="OK" value="OK" onclick="javascript:sendtext()">
</div><!-- zone-message -->
</div><!-- zone-de-conversation -->

43
templates/games.html Normal file
View File

@ -0,0 +1,43 @@
{% extends "base.html" %}
{% block title %} Jeux {% endblock %}
{% block titre %} Liste des jeux {% endblock %}
{% block contenu %}
<div id="zone-de-contenu">
{% if cancreategames %}
<fieldset class="fieldset" ><legend style="font-size:25px;">Ajouter un jeu</legend>
<button id="add" onclick="javascript:go('/add_game')">
<img src="/static/plus.png" style="float:left;" alt=''/><div style="float:right; margin:5px;">Créer un nouveau jeu</div>
</button>
</fieldset>
{% endif %}
{% if mesjeux|length > 0 %}
<fieldset class="fieldset" ><legend style="font-size:25px;">Mes jeux</legend>
<table class="tablelist">
{% for g in mesjeux %}
<tr>
<td width=80%><a href="/game/{{g.id}}"> {{ g.name }} </a></td>
<td width=20%><a href="/game/{{g.id}}"><img src="/static/ok.png" alt="Supprimer"/></a><a href="/game/{{g.id}}/del"><img src="/static/croix.png" alt="Supprimer"/></a></td>
</tr>
{% endfor %}
</table>
</fieldset>
{% endif %}
{% if joinable|length > 0 %}
<fieldset class="fieldset" ><legend style="font-size:25px;">Autres jeux</legend>
<table class="tablelist">
{% for g in joinable %}
<tr><td width=80%><a href="/game/{{g.id}}"> {{ g.name }} </a></td>
<td width=20%><a href="/game/{{g.id}}"><img src="/static/ok.png" alt="Supprimer"/></a> </td>
</tr>
</li>
{% endfor %}
</table>
</fieldset>
{% endif %}
</div>
{% endblock %}

5
templates/head.html Normal file
View File

@ -0,0 +1,5 @@
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta http-equiv="cache-control" content="no-cache" />
<link rel='stylesheet' type='text/css' href='/static/belote.css' />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.0/jquery.min.js" integrity="sha256-xNzN2a4ltkB44Mc/Jz3pT4iU1cmeR0FkXs4pru/JxaQ=" crossorigin="anonymous"></script>
<script type="text/javascript" src="/static/belote-script.js"></script>

12
templates/index.html Normal file
View File

@ -0,0 +1,12 @@
{% extends "base.html" %}
{% block title %} Bienvenue {% endblock %}
{% block titre %} Bienvenue {% endblock %}
{% block contenu %}
<div id="zone-de-contenu">
<p> Bienvenue sur le jeu de belote !</p>
<p> Vous devez vous identifier pour jouer, rendez vous sur <a href="/login">la page de login</a></p>
</div>
{% endblock %}

29
templates/login.html Normal file
View File

@ -0,0 +1,29 @@
{% extends "base.html" %}
{% block title %} Login {% endblock %}
{% block titre %} Login {% endblock %}
{% block contenu %}
<div id="zone-de-contenu">
<form method="POST" action="">
<div style="width:100%;margin:20px;"> </div>
<fieldset class="fieldset"><legend style="font-size:25px;">Identifiez-vous</legend>
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ message }}
</div>
{% endfor %}
<table width=100%>
<tr><td>Nom d'utilisateur : </td><td> <input type="text" name="username" placeholder="Votre nom d'utilisateur" /></td></tr>
<tr><td> Mot de passe : </td><td> <input type="password" name="password" placeholder="Votre mot de passe"/></td></tr>
<tr><td>&nbsp;</td> <td><input id="submit" type="submit" value="&nbsp;Entrer&nbsp;"></td></tr>
</table>
</fieldset>
</form>
</div>
{% endblock %}

27
templates/password.html Normal file
View File

@ -0,0 +1,27 @@
{% extends "base.html" %}
{% block title %} Mot de passe {% endblock %}
{% block titre %} Changement de mot de passe {% endblock %}
{% block contenu %}
<div id="zone-de-contenu">
<div style="width:100%;margin:20px;"> </div>
<fieldset class="fieldset"><legend style="font-size:25px;">Nom du jeu</legend>
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ message }}
</div>
{% endfor %}
<form action="" method="POST" accept-charset="utf-8">
Nouveau mot de passe : <input type="password" name="password" />
<input id="submit" type="submit" value="Valider">
</form>
</fieldset>
</div>
{% endblock %}

1
templates/scripts.html Normal file
View File

@ -0,0 +1 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js" integrity="sha256-bQmrZe4yPnQrLTY+1gYylfNMBuGfnT/HKsCGX+9Xuqo=" crossorigin="anonymous"></script>