Browse Source

Firs commit

Leo VIALLON-GALINIER 11 months ago
commit
b8e4dc2a5d
73 changed files with 2571 additions and 0 deletions
  1. 35 0
      .gitignore
  2. 373 0
      belote.py
  3. 8 0
      belote.wsgi
  4. 148 0
      belote_jeu.py
  5. 168 0
      belote_ws.py
  6. 339 0
      db.py
  7. 7 0
      requirements.txt
  8. 9 0
      settings.exemple.cfg
  9. 73 0
      static/belote-script.js
  10. 391 0
      static/belote.css
  11. BIN
      static/cards/0C.png
  12. BIN
      static/cards/0F.png
  13. BIN
      static/cards/0P.png
  14. BIN
      static/cards/0T.png
  15. BIN
      static/cards/7C.png
  16. BIN
      static/cards/7F.png
  17. BIN
      static/cards/7P.png
  18. BIN
      static/cards/7T.png
  19. BIN
      static/cards/8C.png
  20. BIN
      static/cards/8F.png
  21. BIN
      static/cards/8P.png
  22. BIN
      static/cards/8T.png
  23. BIN
      static/cards/9C.png
  24. BIN
      static/cards/9F.png
  25. BIN
      static/cards/9P.png
  26. BIN
      static/cards/9T.png
  27. BIN
      static/cards/AC.png
  28. BIN
      static/cards/AF.png
  29. BIN
      static/cards/AP.png
  30. BIN
      static/cards/AT.png
  31. BIN
      static/cards/C.png
  32. BIN
      static/cards/DC.png
  33. BIN
      static/cards/DF.png
  34. BIN
      static/cards/DP.png
  35. BIN
      static/cards/DT.png
  36. BIN
      static/cards/F.png
  37. BIN
      static/cards/P.png
  38. BIN
      static/cards/RC.png
  39. BIN
      static/cards/RF.png
  40. BIN
      static/cards/RP.png
  41. BIN
      static/cards/RT.png
  42. BIN
      static/cards/T.png
  43. BIN
      static/cards/VC.png
  44. BIN
      static/cards/VF.png
  45. BIN
      static/cards/VP.png
  46. BIN
      static/cards/VT.png
  47. BIN
      static/croix.png
  48. BIN
      static/edit.png
  49. BIN
      static/fleche-bas.gif
  50. BIN
      static/fleche-haut.gif
  51. 262 0
      static/game_script.js
  52. BIN
      static/logo.gif
  53. BIN
      static/ok.png
  54. BIN
      static/plus.png
  55. 28 0
      templates/admin_game_add.html
  56. 27 0
      templates/admin_games.html
  57. 17 0
      templates/admin_user_del.html
  58. 40 0
      templates/admin_user_edit.html
  59. 25 0
      templates/admin_users.html
  60. 52 0
      templates/base.html
  61. 44 0
      templates/belote.html
  62. 21 0
      templates/game.html
  63. 21 0
      templates/game_add.html
  64. 17 0
      templates/game_del.html
  65. 166 0
      templates/game_join.html
  66. 88 0
      templates/game_start.html
  67. 95 0
      templates/game_zone_jeu.html
  68. 43 0
      templates/games.html
  69. 5 0
      templates/head.html
  70. 12 0
      templates/index.html
  71. 29 0
      templates/login.html
  72. 27 0
      templates/password.html
  73. 1 0
      templates/scripts.html

+ 35 - 0
.gitignore

@@ -0,0 +1,35 @@
1
+# Byte-compiled / optimized / DLL files
2
+__pycache__/
3
+*.py[cod]
4
+
5
+# Translations
6
+*.mo
7
+*.pot
8
+
9
+# pyenv
10
+.python-version
11
+
12
+# Environments
13
+.env
14
+.venv
15
+env/
16
+venv/
17
+ENV/
18
+env.bak/
19
+venv.bak/
20
+
21
+## Editors:
22
+*.bak
23
+*.sav
24
+*.backup
25
+.*.swp
26
+*~
27
+
28
+# Secret configuration
29
+settings.cfg
30
+
31
+# Personal conf
32
+static/jquery.js
33
+static/socket.io.js
34
+templates/head-perso.html
35
+templates/scripts-perso.html

+ 373 - 0
belote.py

@@ -0,0 +1,373 @@
1
+#!/usr/bin/env python3
2
+# -*- coding: utf-8 -*-
3
+
4
+import flask
5
+from flask_socketio import SocketIO
6
+import flask_login as fll
7
+import datetime
8
+from db import db, User, Game, Player, levels_users
9
+from belote_jeu import N_TURN, N_PLAYERS
10
+import belote_ws
11
+
12
+login_manager = fll.LoginManager()
13
+
14
+app = flask.Flask(__name__)
15
+app.config.from_pyfile('settings.cfg')
16
+
17
+#######################################################################
18
+#                          Plugin management                          #
19
+#######################################################################
20
+
21
+# SocketIO
22
+socketio = SocketIO(app)
23
+login_manager.init_app(app)
24
+
25
+# Database management
26
+db.app = app
27
+db.init_app(app)
28
+db.create_all()
29
+db.session.commit()
30
+
31
+# Insert admin if not exist
32
+if User.query.limit(1).first() is None:
33
+    admin = User(login='admin', name='Admin', level=levels_users['Admin'])
34
+    admin.set_password('admin')
35
+    db.session.add(admin)
36
+    db.session.commit()
37
+
38
+for i in range(4):
39
+    if User.query.get('leo'+str(i)) is None:
40
+        leo = User(login='leo'+str(i), name='Leo '+str(i), level=levels_users['Can manage simple users'])
41
+        leo.set_password('leo')
42
+        db.session.add(leo)
43
+db.session.commit()
44
+
45
+# Login management
46
+@login_manager.user_loader
47
+def load_user(user_id):
48
+    if user_id is None :
49
+        return None
50
+    return User.query.get(user_id)
51
+
52
+@login_manager.unauthorized_handler
53
+def unauthorized_callback():
54
+    return flask.redirect('/login?next=' + flask.request.path)
55
+
56
+
57
+#######################################################################
58
+#                    Global variables or fuctions                     #
59
+#######################################################################
60
+URL_DEFAULT = '/games'
61
+namespaces_join = {}
62
+namespaces = {}
63
+
64
+def create_namespace(gid, join=False):
65
+    if(join):
66
+        namespace = belote_ws.CustomNamespaceJoin(gid)
67
+        namespaces_join[gid] = True
68
+        socketio.on_namespace(namespace)
69
+    else:
70
+        namespace = belote_ws.CustomNamespacePlay(gid)
71
+        namespaces[gid] = True
72
+        socketio.on_namespace(namespace)
73
+
74
+
75
+@app.route('/')
76
+def home():
77
+    user = fll.current_user
78
+    if not hasattr(user, 'level'):
79
+        user = None
80
+    return flask.render_template("index.html", user=user)
81
+#######################################################################
82
+#                            Login, logout                            #
83
+#######################################################################
84
+
85
+@app.route('/login', methods=['GET', 'POST'])
86
+def login():
87
+    if fll.current_user:
88
+        flask.redirect(URL_DEFAULT)
89
+    if flask.request.method=="POST":
90
+        username = flask.request.form['username']
91
+        password = flask.request.form['password']
92
+        user =  User.query.get(username)
93
+        if user and user.level > levels_users['Inactive'] and user.check_password(password):
94
+            fll.login_user(user)
95
+            user.last_login = datetime.datetime.utcnow()
96
+            next = flask.request.args.get('next')
97
+            if next is not None and next[0]=='/':
98
+                return flask.redirect(next)
99
+            else:
100
+                return flask.redirect(URL_DEFAULT)
101
+        else:
102
+            app.logger.warning('Login fail for user {} from {}'.format(user, flask.request.remote_addr))
103
+            flask.flash('Invalid username/password combination')
104
+    return flask.render_template('login.html', user=None)
105
+
106
+@app.route('/logout')
107
+def logout():
108
+    fll.logout_user()
109
+    return flask.redirect('/')
110
+
111
+#######################################################################
112
+#                           Games : list                              #
113
+#######################################################################
114
+
115
+@app.route('/games')
116
+@fll.login_required
117
+def games():
118
+    games = Game.query.all()
119
+    mesjeux = []
120
+    joinable = []
121
+    for g in games:
122
+        if g.admin==fll.current_user.login:
123
+            mesjeux.append(g)
124
+        elif g.can_join(fll.current_user):
125
+            joinable.append(g)
126
+    cancreategames = fll.current_user.level >= levels_users['Can create games']
127
+    candeletegames = fll.current_user.level >= levels_users['Admin']
128
+    return flask.render_template('games.html', mesjeux=mesjeux, joinable=joinable, user=fll.current_user, \
129
+            cancreategames=cancreategames, candeletegames=candeletegames)
130
+
131
+#######################################################################
132
+#                   Create, join and play to a game                   #
133
+#######################################################################
134
+
135
+@app.route('/add_game', methods=['GET', 'POST'])
136
+@fll.login_required
137
+def game_add():
138
+    player = fll.current_user
139
+    if fll.current_user.level >= levels_users['Can create games']:
140
+        if flask.request.method=="POST":
141
+            name = flask.request.form['name']
142
+            if name is None or name == '':
143
+                return flask.redirect('/add_game')
144
+            game = Game(name=name, admin=player.login)
145
+            db.session.add(game)
146
+            db.session.commit()
147
+            game.join(fll.current_user)
148
+            db.session.commit()
149
+            return flask.redirect('/game/{}'.format(game.id))
150
+        return flask.render_template('game_add.html', user=fll.current_user)
151
+    return flask.redirect(URL_DEFAULT)
152
+
153
+@app.route('/game/<roomid>/join')
154
+@fll.login_required
155
+def game_page_join(roomid):
156
+    game = Game.query.get(roomid)
157
+    if game:
158
+        player = fll.current_user
159
+        if game.start:
160
+            return flask.redirect('/game/{}'.format(roomid))
161
+        if game.can_join(player):
162
+            if game.id not in namespaces_join:
163
+                create_namespace(game.id, join=True)
164
+            return flask.render_template('game_join.html', game=game, user=player, admin=game.admin)
165
+    return flask.redirect(URL_DEFAULT)
166
+
167
+@app.route('/game/<roomid>/start')
168
+@fll.login_required
169
+def game_page_start(roomid):
170
+    game = Game.query.get(roomid)
171
+    if game:
172
+        player = fll.current_user
173
+        if game.isadmin(player) and game.fixplayers and not game.start:
174
+            return flask.render_template('game_start.html', game=game, user=player)
175
+        return flask.redirect('/game/{}'.format(roomid))
176
+    return flask.redirect(URL_DEFAULT)
177
+
178
+@app.route('/game/<roomid>')
179
+@fll.login_required
180
+def game_page(roomid):
181
+    game = Game.query.get(roomid)
182
+    if game:
183
+        player = fll.current_user
184
+        if not game.start:
185
+            return flask.redirect('/game/{}/join'.format(roomid))
186
+        if game.authorized(player) :
187
+            if game.id not in namespaces:
188
+                create_namespace(game.id)
189
+            return flask.render_template('game.html', game=game, user=player)
190
+    return flask.redirect(URL_DEFAULT)
191
+
192
+@app.route('/game/<roomid>/del')
193
+@fll.login_required
194
+def delgame(roomid):
195
+    game = Game.query.get(roomid)
196
+    if game is not None and fll.current_user.login == game.admin:
197
+        confirm = flask.request.args.get('confirm')
198
+        if confirm is not None:
199
+            db.session.delete(game)
200
+            db.session.commit()
201
+        else:
202
+            return flask.render_template('game_del.html', game=game, user=fll.current_user)
203
+    return flask.redirect(URL_DEFAULT)
204
+
205
+#######################################################################
206
+#                          Users management                           #
207
+#######################################################################
208
+@app.route('/admin/users')
209
+@fll.login_required
210
+def admin_users():
211
+    if fll.current_user.level < levels_users['Can manage simple users']:
212
+        return flask.redirect(URL_DEFAULT)
213
+    users = User.query.all()
214
+    return flask.render_template('admin_users.html', user=fll.current_user, users=users)
215
+
216
+@app.route('/admin/users/<user>', methods=['GET', 'POST'])
217
+@fll.login_required
218
+def edituser(user):
219
+    new = user=='new'
220
+    user = User.query.get(user)
221
+    if not new and user is None:
222
+        return flask.redirect('/admin/users')
223
+    if fll.current_user.level >= levels_users['Admin'] or \
224
+            fll.current_user.level >= levels_users['Can manage simple users'] and \
225
+            fll.current_user.level > user.level:
226
+        if flask.request.method=="POST":
227
+            username = flask.request.form['username']
228
+            name = flask.request.form['name']
229
+            password = flask.request.form['password']
230
+            level = int(flask.request.form['level'])
231
+            if level not in levels_users.values():
232
+                flask.flash('Level unknown')
233
+                return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
234
+            if fll.current_user.level < levels_users['Admin']:
235
+                if not level < fll.current_user.level:
236
+                    flask.flash('Invalid level')
237
+                    return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
238
+            if len(password)<2 and new:
239
+                flask.flash('Mot de passe trop court')
240
+                return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
241
+            if new and User.query.get(username) is not None:
242
+                flask.flash('Login déjà utilisé ! Choisissez un autre !')
243
+                return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
244
+            if new:
245
+                usertoadd = User(login=username, name=name, level=level)
246
+                usertoadd.set_password(password)
247
+                db.session.add(usertoadd)
248
+            else:
249
+                user.name = name
250
+                if len(password)>=2:
251
+                    user.password = password
252
+                user.level = level
253
+            db.session.commit()
254
+            return flask.redirect('/admin/users')
255
+        else:
256
+            return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
257
+    return flask.redirect(URL_DEFAULT)
258
+
259
+@app.route('/admin/users/<user>/del')
260
+@fll.login_required
261
+def deluser(user):
262
+    user = User.query.get(user)
263
+    if user is None:
264
+        return flask.redirect('/admin/users')
265
+    if fll.current_user.level >= levels_users['Admin'] or \
266
+            fll.current_user.level >= levels_users['Can manage simple users'] and \
267
+            fll.current_user.level > user.level:
268
+        confirm = flask.request.args.get('confirm')
269
+        if confirm is not None:
270
+            delete_game(user=user)
271
+            db.session.delete(user)
272
+            db.session.commit()
273
+            return flask.redirect('/admin/users')
274
+    return flask.render_template("admin_user_del.html", user=fll.current_user, useredit=user)
275
+
276
+@app.route('/password', methods=['GET', 'POST'])
277
+@fll.login_required
278
+def change_password():
279
+    if flask.request.method=="POST":
280
+        password = flask.request.form['password']
281
+        if len(password) < 5:
282
+            flask.flash('Mot de passe trop court !')
283
+        else:
284
+            fll.current_user.set_password(password)
285
+            db.session.commit()
286
+            return flask.redirect(URL_DEFAULT)
287
+    return flask.render_template("password.html", user=fll.current_user)
288
+
289
+#######################################################################
290
+#                           Game managament                           #
291
+#######################################################################
292
+@app.route('/admin/games')
293
+@fll.login_required
294
+def admin_games():
295
+    if fll.current_user.level < levels_users['Can manage games']:
296
+        return flask.redirect(URL_DEFAULT)
297
+    games = Game.query.all()
298
+    return flask.render_template('admin_games.html', user=fll.current_user, games=games)
299
+
300
+@app.route('/admin/games/<roomid>/del')
301
+@fll.login_required
302
+def admin_game_del(roomid):
303
+    game = Game.query.get(roomid)
304
+    if game is None:
305
+        return flask.redirect('/admin/games')
306
+    if fll.current_user.level >= levels_users['Can manage games']:
307
+        confirm = flask.request.args.get('confirm')
308
+        if confirm is not None:
309
+            delete_game(game=game)
310
+            return flask.redirect('/admin/games')
311
+        else:
312
+            return flask.render_template('game_del.html', user=fll.current_user, game=game)
313
+    return flask.redirect(URL_DEFAULT)
314
+
315
+@app.route('/game/<roomid>/see')
316
+@fll.login_required
317
+def see_game(roomid):
318
+    game = Game.query.get(roomid)
319
+    if game is None:
320
+        return flask.redirect('/games')
321
+    if fll.current_user.level >= levels_users['Can manage games'] or  game.authorized(fll.current_user) and game.turn==N_TURN*N_PLAYERS:
322
+        return game.serialize_state_anonymous()
323
+    return flask.redirect(URL_DEFAULT)
324
+
325
+@app.route('/admin/games/new', methods=['GET', 'POST'])
326
+@fll.login_required
327
+def admin_games_new():
328
+    if fll.current_user.level < levels_users['Can manage games']:
329
+        return flask.redirect(URL_DEFAULT)
330
+    if flask.request.method=="POST":
331
+        name = flask.request.form['name']
332
+        admin = flask.request.form['admin']
333
+        admin = User.query.get(admin)
334
+        if admin is None:
335
+            flask.flash('Login inconnu')
336
+        elif len(name)<2:
337
+            flask.flash('Nom trop court !')
338
+        else:
339
+            game = Game(name=name, admin=admin.login)
340
+            db.session.add(game)
341
+            db.session.commit()
342
+            game.join(admin)
343
+            db.session.commit()
344
+            return flask.redirect('/admin/games')
345
+    return flask.render_template('admin_game_add.html', user=fll.current_user)
346
+
347
+
348
+
349
+def delete_game(user=None, game=None):
350
+    if user is not None:
351
+        games = Game.query.filter_by(admin=user.login).all()
352
+        for g in games:
353
+            for p in g.players:
354
+                db.session.delete(p)
355
+            db.session.delete(g)
356
+        db.session.commit()
357
+    if isinstance(game, int):
358
+        g = Game.query.get(game)
359
+        if g is not None:
360
+            for p in g.players:
361
+                db.session.delete(p)
362
+            db.session.delete(g)
363
+            db.session.commit()
364
+    elif game is not None:
365
+        for p in game.players:
366
+            db.session.delete(p)
367
+        db.session.delete(game)
368
+        db.session.commit()
369
+
370
+
371
+if __name__ == '__main__':
372
+    socketio.run(app, debug=True, host='0.0.0.0')
373
+

+ 8 - 0
belote.wsgi

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

+ 148 - 0
belote_jeu.py

@@ -0,0 +1,148 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+N_PLAYERS = 4
4
+N_TURN = int(32/4)
5
+
6
+# P : pique, C : Coeur, F : Carreau, T : Trèfle
7
+# 7..9, 0 puis VDRA
8
+cards =['AP', 'RP', 'DP', 'VP', '0P', '9P', '8P', '7P',
9
+        'AC', 'RC', 'DC', 'VC', '0C', '9C', '8C', '7C',
10
+        'AF', 'RF', 'DF', 'VF', '0F', '9F', '8F', '7F',
11
+        'AT', 'RT', 'DT', 'VT', '0T', '9T', '8T', '7T'
12
+        ]
13
+
14
+points_atout = {'V' : 20, '9' : 14, 'A': 11, '0': 10, 'R': 4, 'D': 3, '8':0, '7':0}
15
+points_atout_order = {'V' : 20, '9' : 14, 'A': 11, '0': 10, 'R': 4, 'D': 3, '8':1, '7':0}
16
+points_order = {'A': 11, '0': 10, 'R': 4, 'D': 3, 'V': 2, '9': 1, '8':0.5, '7':0}
17
+points = {'A': 11, '0': 10, 'R': 4, 'D': 3, 'V': 2, '9': 0, '8':0, '7':0}
18
+
19
+translate = {'A': 'As', 'R' : 'Roi', 'D': 'Dame', 'V': 'Valet', '0': '10', 'P': 'Pique', 'C': 'Coeur', 'T':'Trèfle', 'F':'Carreau'}
20
+
21
+colors = ['P', 'C', 'F', 'T']
22
+
23
+def translator(card):
24
+    s = []
25
+    for c in card:
26
+        if c in translate:
27
+            s.append(translate[c])
28
+        else:
29
+            s.append(c)
30
+    return ' '.join(s)
31
+
32
+def winner(cards, atout):
33
+    """
34
+    Returns id winner (0-3), number of points
35
+    """
36
+    if isinstance(cards, str):
37
+        cardsl = cards.split(',')
38
+        cardss = cards
39
+    else:
40
+        cardss = ''.join(cards)
41
+        cardsl = cards
42
+
43
+    if len(cardss)==0:
44
+        return 0, 0
45
+    winner = None
46
+    if atout in cardss:
47
+        for i in range(len(cardsl)):
48
+            if cardsl[i][1] == atout:
49
+                if winner is None or points_atout_order[cardsl[i][0]] > points_atout_order[cardsl[winner][0]]:
50
+                    winner = i
51
+    if winner is None:
52
+        couldemandee = cardss[1]
53
+        for i in range(len(cardsl)):
54
+            if cardsl[i][1] == couldemandee:
55
+                if winner is None or points_order[cardsl[i][0]] > points_order[cardsl[winner][0]]:
56
+                    winner = i
57
+
58
+    pointsr = 0
59
+    for c in cardsl:
60
+        if c[1] == atout:
61
+            pointsr += points_atout[c[0]]
62
+        else:
63
+            pointsr += points[c[0]]
64
+
65
+    return winner, pointsr
66
+
67
+def legal_play(played, atout, card, main):
68
+    if card not in cards:
69
+        return False
70
+    if card not in main:
71
+        return False
72
+
73
+    if played is None :
74
+        playedl = []
75
+        playeds = ''
76
+    elif isinstance(played, str):
77
+        playedl = played.split(',')
78
+        playeds = played
79
+    else:
80
+        playeds = ''.join(played)
81
+        playedl = played
82
+
83
+    if main is None or main=='':
84
+        main = []
85
+    if isinstance(main, str):
86
+        mains = main
87
+        main = main.split(',')
88
+    else:
89
+        mains = ','.join(main)
90
+
91
+    # Le premier a jouer fait ce qu'il veut
92
+    if len(playeds)==0:
93
+        return True
94
+    couldemandee =playeds[1]
95
+
96
+    # On regarde les atouts pour plus tard:
97
+    maxpose = -1
98
+    for c in playedl:
99
+        if c[1]==atout and points_atout_order[c[0]]>maxpose:
100
+            maxpose = points_atout_order[c[0]]
101
+
102
+    peutmonter = False
103
+    has_atout = False
104
+    for m in main: #Peut-on monter ?
105
+        if m[1] == atout:
106
+            has_atout = True
107
+            if points_atout_order[m[0]]>maxpose:
108
+                peutmonter = True
109
+                break
110
+
111
+    # Il faut jouer de la couleur demandée
112
+    if couldemandee != atout:
113
+        if couldemandee in mains:
114
+            if card[1]== couldemandee:
115
+                return True
116
+            else:
117
+                return False
118
+    if couldemandee == atout:
119
+        # Il faut jouer atout (si possible)
120
+        if has_atout:
121
+            if card[1] != atout:
122
+                return False
123
+        # Et monter ! (si possible)
124
+        if peutmonter:
125
+            if points_atout_order[card[0]]<=maxpose:
126
+                return False
127
+        # Mais si on a pas, tant pis !
128
+        return True
129
+
130
+    # Sinon, mon partenaire est-il maitre ?
131
+    if len(playedl)>=2:
132
+        maitre, xxx = winner(playedl, atout)
133
+        if maitre == len(playedl)-2 :
134
+            # Si oui, jeu libre
135
+            return True
136
+
137
+    # Sinon, ai-je de l'atout ?
138
+    if has_atout:
139
+        if card[1] != atout:
140
+            return False
141
+        # Si oui, je dois monter si possible
142
+        if peutmonter:
143
+            if points_atout_order[card[0]] < maxpose:
144
+                return False
145
+        return True
146
+
147
+    # Sinon, jeu libre !
148
+    return True

+ 168 - 0
belote_ws.py

@@ -0,0 +1,168 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+import flask_socketio as fio
4
+from flask_login import current_user
5
+from db import Game, Player, User
6
+from belote_jeu import translator, legal_play, colors, N_PLAYERS, N_TURN
7
+
8
+NAME_ARBITRE = 'Arbitre'
9
+NAME_BONJOUR = 'Portier'
10
+
11
+class CustomNamespacePlay(fio.Namespace):
12
+    def __init__(self, id):
13
+        self._id = id
14
+        self.users = []
15
+        fio.Namespace.__init__(self, '/game/{}'.format(id))
16
+    def on_connect(self):
17
+        if current_user is None or not self.game().authorized(current_user):
18
+            fio.disconnect()
19
+        if current_user.name not in self.users:
20
+            if len(self.users)==1:
21
+                fio.emit('text', {'text': '{} est là !'.format(self.users[0]), 'username':None, 'name':NAME_BONJOUR})
22
+            elif len(self.users)>1:
23
+                fio.emit('text', {'text': '{} est là !'.format(', '.join(self.users)), 'username':None, 'name':NAME_BONJOUR})
24
+            self.users.append(current_user.name)
25
+            fio.emit('text', {'text': '{} vient d\'arriver !'.format(current_user.name), 'username':None, 'name':NAME_BONJOUR}, broadcast=True)
26
+        pass
27
+
28
+    def on_disconnect(self):
29
+        if current_user.name in self.users:
30
+            self.users.remove(current_user.name)
31
+            fio.emit('text', {'text': '{} est parti.'.format(current_user.name), 'username':None, 'name':NAME_BONJOUR}, broadcast=True)
32
+        pass
33
+
34
+    def on_join(self, data):
35
+        pass
36
+
37
+    def on_choose_color(self, data):
38
+        if 'atout' not in data:
39
+            return
40
+        atout = data['atout']
41
+
42
+        game = self.game()
43
+        if not game.turn<0:
44
+            fio.emit('text', {'text': "Choix d'atout indisponible", 'username':None, 'name':NAME_ARBITRE})
45
+            return
46
+        # C'est mon tour ?
47
+        if game.get_nr(current_user) != (game.turn + game.first_player)%4:
48
+            fio.emit('text', {'text': "Ce n'est pas votre tour...", 'username':None, 'name':NAME_ARBITRE})
49
+            return
50
+        # Choix légal pour ce tour ?
51
+        if atout is not None and game.turn < -4 and game.played[1] != atout:
52
+            fio.emit('text', {'text': "Pour l'instant on prend ou pas, mais on ne peut pas choisir l'atout.", 'username':None, 'name':NAME_ARBITRE})
53
+            return
54
+        if atout is not None and not atout in colors:
55
+            fio.emit('text', {'text': "Couleur d'atout inconnue ({})".format(atout), 'username':None, 'name':NAME_ARBITRE})
56
+            return
57
+        # OK !
58
+        print('atout choix')
59
+        print(atout)
60
+        if(game.tour_choix(atout, current_user)):
61
+            print('Emit atout choix')
62
+            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)
63
+        else:
64
+            fio.emit('text', {'text': "Erreur E0101", 'username':None, 'name':NAME_ARBITRE})
65
+    
66
+    def on_play(self, data):
67
+        if 'card' not in data:
68
+            return
69
+        card = data['card']
70
+
71
+        game = self.game()
72
+        if game.turn<0:
73
+            fio.emit('text', {'text': "Jeu pas encore disponible. Il faut choisir l'atout !", 'username':None, 'name':NAME_ARBITRE})
74
+            return
75
+        # C'est mon tour ?
76
+        if game.get_nr(current_user) != (game.turn + game.first_player)%4:
77
+            fio.emit('text', {'text': "Ce n'est pas votre tour...", 'username':None, 'name':NAME_ARBITRE})
78
+            return
79
+        # Choix légal pour ce tour ?
80
+        p = game.get_player(current_user)
81
+        if legal_play(game.played, game.atout, card, p.cards):
82
+            ok, winnr = game.tour_jeu(card, current_user)
83
+            if ok:
84
+                fio.emit('play', {'turn':game.turn, 'played':game.played.split(',') if game.played is not None and game.played !='' else [], \
85
+                        '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)
86
+        else:
87
+            fio.emit('text', {'text': "Vous ne pouvez pas jouer cette carte ({})".format(translator(card)), 'username':None, 'name':NAME_ARBITRE})
88
+            return
89
+
90
+    def on_text(self, data):
91
+        if 'text' in data and len(data['text'])>0:
92
+            fio.emit('text', {'text':data['text'], 'username':current_user.login, 'name':current_user.name}, broadcast=True);
93
+
94
+    def on_monjeu(self, data):
95
+        cards = self.game().get_player(current_user).cards
96
+        cards = cards.split(',') if cards is not None and cards != '' else []
97
+        fio.emit('monjeu', {'cards':cards});
98
+
99
+    def on_restart(self, data):
100
+        game = self.game()
101
+        if game.isadmin(current_user):
102
+            if game.turn==N_TURN*N_PLAYERS:
103
+                game.restart_jeu()
104
+                fio.emit('restart', game.serialize_state_anonymous(), broadcast=True)
105
+            else:
106
+                fio.emit('text', {'text': "Finissez la partie avant d'en recommencer une.", 'username':None, 'name':NAME_ARBITRE})
107
+        else:
108
+            fio.emit('text', {'text': "Demandez au maitre du jeu pour reprendre une partie.", 'username':None, 'name':NAME_ARBITRE})
109
+
110
+    def game(self):
111
+        return Game.query.get(self._id)
112
+
113
+
114
+class CustomNamespaceJoin(fio.Namespace):
115
+    def __init__(self, id):
116
+        self._id = id
117
+        fio.Namespace.__init__(self, '/game/{}/join'.format(id))
118
+    def on_connect(self):
119
+        pass
120
+
121
+    def on_disconnect(self):
122
+        pass
123
+
124
+    def on_join(self, data):
125
+        if self.game().can_join(current_user):
126
+            if not self.game().authorized(current_user):
127
+                self.game().join(current_user)
128
+                fio.emit('join', {'username':current_user.login, 'name': current_user.name}, broadcast=True)
129
+
130
+    def on_add_player(self, data):
131
+        if self.game().isadmin(current_user):
132
+            if 'username' in data:
133
+                user = User.query.get(data['username'])
134
+                if user is not None and self.game().can_join(user):
135
+                    if not self.game().authorized(user):
136
+                        self.game().join(user)
137
+                        fio.emit('join', {'username':user.login, 'name': user.name}, broadcast=True)
138
+
139
+    def on_leave(self, data):
140
+        if self.game().leave(current_user):
141
+            fio.emit('leave', {'username':current_user.login}, broadcast=True)
142
+
143
+    def on_ban(self, data):
144
+        if 'username' in data:
145
+            username = data['username']
146
+            if self.game().isadmin(current_user):
147
+                if 'username' is not None:
148
+                    if self.game().leave(username):
149
+                        fio.emit('leave', {'username':username})
150
+
151
+    def on_fixplayers(self, data):
152
+        if self.game().isadmin(current_user):
153
+            if self.game().can_start():
154
+                self.game().fix_players()
155
+                fio.emit('fixplayers', {})
156
+
157
+    def on_start(self, data):
158
+        if 'order' in data:
159
+            order = data['order']
160
+        else:
161
+            order = None
162
+        if self.game().isadmin(current_user):
163
+            if self.game().start_game(order):
164
+                fio.emit('start', {}, broadcast=True)
165
+
166
+    def game(self):
167
+        return Game.query.get(self._id)
168
+    

+ 339 - 0
db.py

@@ -0,0 +1,339 @@
1
+from flask_login import UserMixin
2
+from werkzeug.security import generate_password_hash, check_password_hash
3
+from flask_sqlalchemy import SQLAlchemy
4
+from sqlalchemy.orm import relationship
5
+import random
6
+from belote_jeu import cards, N_PLAYERS, colors, winner, N_TURN
7
+
8
+db = SQLAlchemy()
9
+
10
+levels_users = {'Inactive':0, 'Simple user':1, 'Can create games':2, 'Can manage simple users': 8, 'Can manage games':9, 'Admin':10}
11
+
12
+#######################################################################
13
+#                             User class                              #
14
+#######################################################################
15
+
16
+class User(UserMixin, db.Model):
17
+    """Model for user accounts."""
18
+
19
+    __tablename__ = 'users'
20
+
21
+    login = db.Column(db.String(200),
22
+                   primary_key=True)
23
+    name = db.Column(db.String(200),
24
+                     nullable=False,
25
+                     unique=False)
26
+    password = db.Column(db.String(200),
27
+                         primary_key=False,
28
+                         unique=False,
29
+                         nullable=False)
30
+    level = db.Column(db.Integer, default=1)
31
+    last_login = db.Column(db.DateTime)
32
+
33
+    def set_password(self, password):
34
+        """Create hashed password."""
35
+        self.password = generate_password_hash(password, method='sha256')
36
+
37
+    def check_password(self, password):
38
+        """Check hashed password."""
39
+        return check_password_hash(self.password, password)
40
+
41
+    def get_id(self):
42
+        return self.login
43
+
44
+    def __repr__(self):
45
+        return '<User {}>'.format(self.login)
46
+
47
+#######################################################################
48
+#                             Game class                              #
49
+#######################################################################
50
+
51
+class Game(db.Model):
52
+    __tablename__ = 'games'
53
+
54
+    id = db.Column(db.Integer, 
55
+            primary_key=True)
56
+    name = db.Column(db.String(200))
57
+    players = relationship("Player")
58
+    turn = db.Column(db.Integer, default=-8)
59
+    atout = db.Column(db.String(1))
60
+    preneur = db.Column(db.Integer)
61
+    admin = db.Column(db.String(200), db.ForeignKey('users.login'))
62
+    cards_to_distribute = db.Column(db.String(200))
63
+    first_player = db.Column(db.Integer)
64
+    start = db.Column(db.Boolean, default=False)
65
+    last_played = db.Column(db.String(20))
66
+    played = db.Column(db.String(20))
67
+    fixplayers = db.Column(db.Boolean, default=False)
68
+    points_0 = db.Column(db.Integer, default=0)
69
+    points_1 = db.Column(db.Integer, default=0)
70
+    cumul_0 = db.Column(db.Integer, default=0)
71
+    cumul_1 = db.Column(db.Integer, default=0)
72
+    partie = db.Column(db.Integer, default=1)
73
+
74
+    def start_game(self, ordered_players):
75
+        # Ordering players
76
+        if self.fixplayers and not self.start:
77
+            i=0
78
+            if ordered_players is not None:
79
+                for op in ordered_players:
80
+                    player = Player.query.filter_by(game=self.id, user=op).first()
81
+                    if player is not None:
82
+                        player.nr = i
83
+                        i+=1
84
+                for p in self.players:
85
+                    if p.nr is None or p.nr<0:
86
+                        p.nr = i
87
+                        i+=1
88
+            db.session.commit()
89
+
90
+            self.start = True
91
+            self.first_player = 0
92
+            self.distribute()
93
+            db.session.commit()
94
+            return True
95
+        return False
96
+
97
+    def fix_players(self):
98
+        if self.can_start():
99
+            self.fixplayers = True
100
+            db.session.commit()
101
+
102
+    def distribute(self):
103
+        ___cartes = random.sample(cards, k=len(cards))
104
+        for i in range(len(self.players)):
105
+            self.players[i].cards = ','.join(___cartes[5*i:5*(i+1)])
106
+        self.played = ___cartes[5*(i+1)]
107
+        self.cards_to_distribute = ','.join(___cartes[5*(i+1)+1:])
108
+
109
+    def authorized(self, user):
110
+        for p in self.players:
111
+            if user.login == p.user:
112
+                return True
113
+        return False
114
+    
115
+    def isadmin(self, user):
116
+        return user.login == self.admin
117
+
118
+    def serialize_state_anonymous(self):
119
+        r = {}
120
+        r['atout'] = self.atout
121
+        r['preneur'] = self.preneur
122
+        r['last_played'] = self.last_played.split(',') if self.last_played is not None else [] 
123
+        r['played'] = self.played.split(',') if self.played is not None else []
124
+        r['turn'] = self.turn
125
+        r['first_player'] = self.first_player
126
+        r['points'] = [self.points_0, self.points_1]
127
+        r['cumul'] = [self.cumul_0, self.cumul_1]
128
+        r['partie'] = self.partie
129
+        return r
130
+
131
+    def serialize_state(self, user):
132
+        r = self.serialize_state_anonymous()
133
+        r['players'] = self.get_ordered_players()
134
+        r['playersinfo'] = {}
135
+        for p in self.players:
136
+            r['playersinfo'][p.user] = User.query.get(p.user).name
137
+        r['nr'] = self.get_nr(user)
138
+        r['admin'] = self.admin
139
+        cards = self.get_player(user).cards
140
+        r['cards'] = cards.split(',') if cards is not None else []
141
+        return r
142
+
143
+    def get_ordered_players(self):
144
+        r = {}
145
+        for p in self.players:
146
+            r[p.nr] = p.user
147
+            r2 = []
148
+        for i in range(N_PLAYERS):
149
+            r2.append(r[i] if i in r else None)
150
+        return r2
151
+
152
+    def get_nr(self, user):
153
+        p = self.get_player(user)
154
+        if p is not None :
155
+            return p.nr
156
+        else:
157
+            return -1
158
+
159
+    def get_player(self, user):
160
+        if not isinstance(user, str):
161
+            user = user.login
162
+        return Player.query.filter_by(game=self.id, user=user).first()
163
+
164
+    def can_join(self, user):
165
+        if self.authorized(user):
166
+            return True
167
+        if not self.fixplayers and len(self.players) < N_PLAYERS:
168
+            return True
169
+        return False
170
+
171
+    def can_start(self):
172
+        if len(self.players)==N_PLAYERS:
173
+            return True
174
+        return False
175
+
176
+    def join(self, user):
177
+        if not self.fixplayers and not self.authorized(user):
178
+            p = Player(game=self.id, user=user.login)
179
+            db.session.add(p)
180
+            db.session.commit()
181
+
182
+    def leave(self, user):
183
+        if not self.fixplayers:
184
+            for p in self.players:
185
+                if isinstance(user, str) and user != self.admin or hasattr(user, 'login') and user.login != self.admin:
186
+                    if isinstance(user, str) and user == p.user or hasattr(user, 'login') and user.login == p.user:
187
+                        db.session.delete(p)
188
+                        db.session.commit()
189
+                        return True
190
+        return False
191
+
192
+    def get_players(self):
193
+        r = []
194
+        for p in self.players:
195
+            user = User.query.get(p.user)
196
+            r.append({'username':user.login, 'name':user.name})
197
+        return r
198
+    
199
+    def tour_choix(self, atout, user):
200
+        if (self.turn + self.first_player) % N_PLAYERS == self.get_nr(user):
201
+            self.turn += 1
202
+            if atout is not None and atout in colors:
203
+                self.atout = atout
204
+                self.preneur = user.login
205
+                self.turn = 0
206
+                self.distribute_restant()
207
+            db.session.commit()
208
+            return True
209
+        return False
210
+
211
+    def distribute_restant(self):
212
+        todistribute = self.cards_to_distribute.split(',')
213
+        for pn in self.get_ordered_players():
214
+            p = self.get_player(pn)
215
+            if pn == self.preneur:
216
+                p.cards += ',' + ','.join(todistribute[:2])
217
+                p.cards += ',' + self.played
218
+                todistribute = todistribute[2:]
219
+            else:
220
+                p.cards += ',' + ','.join(todistribute[:3])
221
+                todistribute = todistribute[3:]
222
+        self.cards_to_distribute = ''
223
+        self.played = None
224
+
225
+    def tour_jeu(self, carte, user):
226
+        if (self.turn + self.first_player) % N_PLAYERS == self.get_nr(user):
227
+            winnr = -1
228
+            self.turn += 1
229
+            if self.played is None or self.played == '':
230
+                self.played = carte
231
+            else:
232
+                self.played += ',' + carte
233
+            if self.turn % N_PLAYERS == 0 :
234
+                winnr, points = winner(self.played, self.atout)
235
+                if (self.first_player + winnr)%2 == 0:
236
+                    self.points_0 += points
237
+                else:
238
+                    self.points_1 += points
239
+                self.last_played = self.played
240
+                if len(self.cards_to_distribute)>1:
241
+                    self.cards_to_distribute += ','
242
+                self.cards_to_distribute += self.played
243
+                self.played = None
244
+                self.first_player = (self.first_player + winnr)%4
245
+            if self.turn == N_TURN*N_PLAYERS: # Dix de der, chute, belote et capote !
246
+                if (self.first_player + winnr)%2 == 0:
247
+                    self.points_0 += 10
248
+                else:
249
+                    self.points_1 += 10
250
+                if self.get_player(self.preneur).nr %2 ==0 and self.points_0 < self.points_1:
251
+                    self.points_0 = 0
252
+                    self.points_1 = 162
253
+                if self.get_player(self.preneur).nr %2 ==1 and self.points_1 < self.points_0:
254
+                    self.points_0 = 162
255
+                    self.points_1 = 0
256
+                if self.points_0 == 162 or self.points_1 == 162:
257
+                    winequipe = 0 if self.points_0 == 162 else 1
258
+                    # Capote possible.
259
+                    winnr = 0
260
+                    ccc = self.cards_to_distribute.split(',')
261
+                    for i in range(N_TURN):
262
+                        win, xxx = winner(ccc[i*N_PLAYERS:(i+1)*N_PLAYERS], self.atout)
263
+                        winnr = win+winnr
264
+                        if winnr % 2 != winequipe:
265
+                            break
266
+                    else:
267
+                        if winequipe == 0:
268
+                            self.points_0 += 90
269
+                        else:
270
+                            self.points_1 += 90
271
+                # belote possible
272
+                winnr = 0
273
+                belote =-1
274
+                ccc = self.cards_to_distribute.split(',')
275
+                for i in range(N_TURN):
276
+                    cccc = ccc[i*N_PLAYERS:(i+1)*N_PLAYERS] 
277
+                    if 'R'+self.atout in cccc:
278
+                        if belote>=0:
279
+                            if (cccc.index('R'+self.atout) + winnr)%4 == belote %4:
280
+                                if belote %2 ==0:
281
+                                    self.points_0 += 20
282
+                                else:
283
+                                    self.points_1 += 20
284
+                        else:
285
+                            belote = winnr + cccc.index('R'+self.atout)
286
+                    if 'D'+self.atout in cccc:
287
+                        if belote>=0:
288
+                            if (cccc.index('D'+self.atout) + winnr)%4 == belote %4:
289
+                                if belote %2 ==0:
290
+                                    self.points_0 += 20
291
+                                else:
292
+                                    self.points_1 += 20
293
+                        else:
294
+                            belote = winnr + cccc.index('D'+self.atout)
295
+                    win, xxx = winner(cccc, self.atout)
296
+                    winnr = win+winnr
297
+            # Retirer la carte du jeu !
298
+            p = self.get_player(user)
299
+            ccc = p.cards
300
+            ccc = ccc.split(',')
301
+            ccc.remove(carte)
302
+            p.cards = ','.join(ccc)
303
+            db.session.commit()
304
+            return True, winnr
305
+        return False, -1
306
+    
307
+    def restart_jeu(self):
308
+        if self.turn==N_TURN*N_PLAYERS:
309
+            self.turn = -8
310
+            self.atout = None
311
+            self.preneur = None
312
+            self.last_played = None
313
+            self.played = None
314
+            self.cumul_0 += self.points_0
315
+            self.cumul_1 += self.points_1
316
+            self.points_0 = 0
317
+            self.points_1 = 0
318
+            self.partie +=1
319
+            self.first_player = 0
320
+            self.distribute()
321
+            db.session.commit()
322
+            return True
323
+        return False
324
+
325
+#######################################################################
326
+#                            Player class                             #
327
+#######################################################################
328
+
329
+class Player(db.Model):
330
+    __tablename__ = 'players'
331
+
332
+    id = db.Column(db.Integer,
333
+            primary_key=True)
334
+    game = db.Column(db.Integer, db.ForeignKey('games.id'))
335
+    user = db.Column(db.Integer, db.ForeignKey('users.login'))
336
+    cards = db.Column(db.String(40))
337
+    nr = db.Column(db.Integer, default=-1)
338
+
339
+

+ 7 - 0
requirements.txt

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

+ 9 - 0
settings.exemple.cfg

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

+ 73 - 0
static/belote-script.js

@@ -0,0 +1,73 @@
1
+/*************************
2
+   la Belote
3
+**************************/
4
+var go = function(url) {
5
+    document.location.href=url;
6
+}
7
+
8
+$(document).ready(function() {
9
+/*==============================
10
+listeners à activer à ready 
11
+===============================*/
12
+
13
+document.getElementById("username").addEventListener("click",function() {apparaitDisparaitMenu('username-items','username-fleche')});
14
+
15
+var options = document.getElementById("options");
16
+if(options){
17
+    options.addEventListener("click",function() {apparaitDisparaitAccordeon('options-valeur','options-fleche')});
18
+}
19
+var derpli = document.getElementById("dernier-pli")
20
+if(derpli){
21
+    derpli.addEventListener("click",function() {apparaitDisparaitAccordeon('dernier-pli-valeur','dernier-pli-fleche')});
22
+}
23
+
24
+
25
+/*==============================
26
+fonctions 
27
+===============================*/
28
+/* 
29
+fonction  apparaitDisparaitUserAttr 
30
+apparition/disparition d'un menu
31
+*/
32
+function apparaitDisparaitMenu (idMenu,idFleche) {
33
+	var element = document.getElementById(idMenu);
34
+	if(element.style.visibility == "hidden" || element.style.visibility == "") {
35
+		document.getElementById(idMenu).style.visibility = "visible";
36
+	}
37
+	else {
38
+		document.getElementById(idMenu).style.visibility = "hidden";
39
+	};
40
+	var elementFleche = document.getElementById(idFleche);
41
+	var splitElement = elementFleche.src.split('/');
42
+	var lastElement = splitElement[splitElement.length-1];
43
+	if(lastElement == "fleche-bas.gif") {
44
+		document.getElementById(idFleche).src = "/static/fleche-haut.gif";
45
+	}
46
+	else {
47
+		document.getElementById(idFleche).src = "/static/fleche-bas.gif";
48
+	}
49
+	return(0);
50
+}
51
+
52
+function apparaitDisparaitAccordeon (idZone,idFleche) {
53
+	var element = document.getElementById(idZone);
54
+	if(element.style.display == "none" || element.style.display == "") {
55
+		document.getElementById(idZone).style.display = "block";
56
+	}
57
+	else {
58
+		document.getElementById(idZone).style.display = "none";
59
+	};
60
+	var elementFleche = document.getElementById(idFleche);
61
+	var splitElement = elementFleche.src.split('/');
62
+	var lastElement = splitElement[splitElement.length-1];
63
+	if(lastElement == "fleche-bas.gif") {
64
+		document.getElementById(idFleche).src = "/static/fleche-haut.gif";
65
+	}
66
+	else {
67
+		document.getElementById(idFleche).src = "/static/fleche-bas.gif";
68
+	}
69
+	return(0);
70
+}
71
+
72
+/* fin de $(document).ready() */
73
+});

+ 391 - 0
static/belote.css

@@ -0,0 +1,391 @@
1
+/* =========================
2
+     la Belote
3
+========================= */
4
+/****************/
5
+/*    Reset     */
6
+/****************/
7
+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}
8
+body{line-height:1}
9
+ol,ul{list-style:none}
10
+blockquote,q{quotes:none}
11
+blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}
12
+:focus{outline:0}
13
+ins{text-decoration:none}
14
+del{text-decoration:line-through}
15
+table{border-collapse:collapse;border-spacing:0}
16
+/****************/
17
+/*  généralités */
18
+/****************/
19
+body {
20
+	margin: 0px;
21
+	background-color: #13995E;
22
+}
23
+div.clear {
24
+	clear: both;
25
+}
26
+div.clearfix-head::after {
27
+	content:"";
28
+	clear: both;
29
+	display: table;
30
+        border: 1px solid black;
31
+        width: 100%;
32
+}
33
+div.clearfix::after{
34
+        content:"";
35
+	clear: both;
36
+	display: table;
37
+}
38
+div, p, li, a, th, td {
39
+	font-family: 'Segoe UI', Arial, Helvetica, sans-serif;
40
+	font-size: 15px;
41
+	line-height: 17px;
42
+	color: #092E1E;
43
+}
44
+/****************/
45
+/* conteneur    */
46
+/****************/
47
+#conteneur { 
48
+	margin-top: 8px;
49
+	margin-right: auto;
50
+	margin-bottom: 0px;
51
+	margin-left: auto;
52
+	height: 600px;
53
+	width: 1200px;
54
+	/* border: 1px solid #092E1E; */
55
+}
56
+/****************/
57
+/* entete       */
58
+/****************/
59
+#entete {
60
+	position: relative;
61
+	height: 66px;
62
+	margin: 2px;
63
+/*
64
+	border: 1px solid #092E1E;
65
+*/
66
+}
67
+#logo {
68
+	margin: 2px 10px 2px 2px;
69
+	float: left;
70
+}
71
+#logo img {
72
+	height: 60px;
73
+	width: 60px;
74
+	display: block;
75
+	margin-left: auto;
76
+	margin-right: auto;
77
+}
78
+#titre {
79
+	width: 55%;
80
+	line-height: 60px;
81
+	margin: 2px 15px 2px 2px;
82
+	text-align: center;
83
+	font-size: 140%;
84
+	font-weight: bold;
85
+	float: left;
86
+}
87
+#username {
88
+	width: 20%;
89
+	height: 60px;
90
+	line-height: 60px;
91
+	margin: 2px 15px 2px 30px;
92
+	float: right;
93
+	
94
+	font-size: 140%;
95
+	font-weight: bold;
96
+	text-align: right;
97
+}
98
+#username-fleche {
99
+	height: 14px;
100
+	width: 14px;
101
+	cursor: pointer;
102
+	padding-left: 10px;
103
+	vertical-align: baseline;
104
+}
105
+#username-items {
106
+	position: absolute; 
107
+	top: 68px;
108
+	right: 0px;
109
+	width: 180px;
110
+	visibility: hidden;
111
+	margin-top: 2px ;
112
+	padding: 5px;
113
+	border: 1px solid #092E1E;
114
+	overflow: hidden;
115
+	z-index: 100;
116
+	background-color: #b7a86b;
117
+}
118
+.menu-item:hover {
119
+	cursor: pointer;
120
+	background-color: #15A160;
121
+}
122
+
123
+/****************/
124
+/* zone-de-contenu  */
125
+/****************/
126
+#zone-de-contenu {
127
+    width:50%;
128
+    margin:auto;
129
+}
130
+#zone-de-contenu>div {
131
+    margin:auto;
132
+}
133
+.tablelist {
134
+    width:100%;
135
+}
136
+/****************/
137
+/* zone-de-jeu  */
138
+/****************/
139
+#zone-de-jeu {
140
+	height: 438px;
141
+	margin: 2px;
142
+/*
143
+	border: 1px solid #092E1E;
144
+*/
145
+}
146
+#plateau {
147
+	height: 320px;
148
+	width: 740px;
149
+	position: relative;
150
+	margin: 3px;
151
+}
152
+#joueur-0 {
153
+	position: absolute;
154
+	top: 55px; 
155
+	left: 190px;
156
+	text-align: right;
157
+}
158
+#joueur-1 {
159
+	position: absolute;
160
+	top: 145px; 
161
+	left: 80px; 
162
+	text-align: right;
163
+}
164
+#joueur-2 {
165
+	position: absolute;
166
+	top: 245px; 
167
+	left: 190px; 
168
+	text-align: right;
169
+}
170
+#joueur-3 {
171
+	position: absolute;
172
+	top: 145px; 
173
+	left: 500px; 
174
+}
175
+#jeu-0 {
176
+	position: absolute;
177
+	top: 30px; 
178
+	left: 300px; 
179
+}
180
+#jeu-1 {
181
+	position: absolute;
182
+	top: 120px; 
183
+	left: 190px; 
184
+}
185
+#jeu-2 {
186
+	position: absolute;
187
+	top: 220px; 
188
+	left: 300px; 
189
+}
190
+#jeu-3 {
191
+	position: absolute;
192
+	top: 120px; 
193
+	left: 420px; 
194
+}
195
+#suivant {
196
+position: absolute;
197
+	bottom: 15px; 
198
+	right: 90px; 
199
+}
200
+#suivant input {
201
+	width: 80px;
202
+}
203
+
204
+#ov {
205
+	height: 310px;
206
+	width: 390px;
207
+	padding: 5px 25px 5px 25px;
208
+	position: relative;
209
+	top: -322px;
210
+	left: 746px;
211
+	margin: 2px;
212
+	border: 1px solid #092E1E;
213
+	
214
+}
215
+#table-atout {
216
+	width: 98%;
217
+}
218
+#table-atout td {
219
+	padding: 4px;
220
+	text-align: center;
221
+	vertical-align: middle;
222
+}
223
+#atout {
224
+	margin: 0px auto 0px auto;
225
+}
226
+#choix {
227
+	height: 160px;
228
+	margin-top: 25px;
229
+	padding: 6px;
230
+	border: 1px solid #092E1E;
231
+	overflow-x: hidden;
232
+	overflow-y: auto;
233
+}
234
+#options-fleche {
235
+	width: 11px;
236
+	height: 11px; 
237
+	float: right;
238
+	cursor: pointer;
239
+	vertical-align: baseline;
240
+}
241
+#options-valeur {
242
+	display: none; 
243
+}
244
+#dernier-pli-fleche {
245
+	width: 11px;
246
+	height: 11px; 
247
+	float: right;
248
+	cursor: pointer;
249
+	vertical-align: baseline;
250
+}
251
+#dernier-pli-valeur {
252
+	display: none; 
253
+}
254
+#dernier-pli {
255
+	margin-top: 10px;
256
+}
257
+#dernier-pli-valeur {
258
+	padding-left: 50px;
259
+}
260
+#jeu-en-main {
261
+	height: 76px;
262
+	width: 642px; 
263
+	position: relative;
264
+	top: -320px;
265
+	left: 0px; 
266
+	margin: 2px;
267
+	padding: 15px 58px 15px 40px;
268
+	border: 1px solid #092E1E;
269
+}
270
+#a-vous {
271
+	float: left;
272
+	width: 90px;
273
+	height: 79px;
274
+	padding-top: 20px;
275
+	text-align: center;
276
+	margin-right: 20px;
277
+	visibility: hidden;
278
+}
279
+#points {
280
+	height: 98px;
281
+	width: 390px;
282
+	position: relative;
283
+	top: -430px;
284
+	left: 746px; 
285
+	margin: 2px;
286
+	border: 1px solid #092E1E;
287
+	padding: 4px 25px 4px 25px;
288
+}
289
+#points p {
290
+	text-align: center;
291
+	margin: 5px 0px 5px 0px;
292
+}
293
+#points table {
294
+	width: 98%;
295
+	border-collapse: collapse;
296
+	border: 1px solid #092E1E;
297
+}
298
+#points table td  {
299
+	padding: 0px 2px 0px 2px;
300
+	text-align: center;
301
+	border: 1px solid #092E1E;
302
+}
303
+.joueur {
304
+	width: 90px;
305
+	height: 18px;
306
+	overflow: hidden;
307
+	text-decoration: underscore;
308
+}
309
+.carte {
310
+	height: 77px;
311
+	width: 50px;
312
+	/* carte : hauteur = 1,54 * largueur;  */
313
+	border: 1px solid #092E1E;
314
+	background-color: #117F4A;
315
+}
316
+.carte-en-main, .carte-dernier-pli {
317
+	float: left;
318
+	margin-right: 12px;
319
+}
320
+.item-choix {
321
+	padding: 4px;
322
+	border: 1px solid #092E1E;
323
+}
324
+.item-choix-valeur {
325
+	padding: 4px;
326
+	border-right: 1px solid #092E1E;
327
+	border-bottom: 1px solid #092E1E;
328
+	border-left: 1px solid #092E1E;
329
+}
330
+/************************/
331
+/* zone-de-conversation */
332
+/************************/
333
+#zone-de-conversation {
334
+	height: 84px;
335
+	margin: 2px;
336
+/*
337
+	border: 1px solid #092E1E;
338
+*/
339
+	position: relative;
340
+}
341
+#chat {
342
+	width: 61%;
343
+	height: 87%;
344
+	float: left;
345
+	background-color: #C4E8D7;
346
+	margin: 2px 15px 2px 2px;
347
+	border-top: 2px solid #888;
348
+	border-right: 2px solid #DDD;
349
+	border-bottom: 2px solid #DDD;
350
+	border-left: 2px solid #888;
351
+	position: absolute;
352
+	overflow-x: hidden;
353
+	overflow-y: auto;
354
+}
355
+#message {
356
+	width: 34%;
357
+	float: right;
358
+	margin: 1px 15px 2px 2px;
359
+	position: absolute; 
360
+	right: 2px;
361
+	top: 3px;
362
+}
363
+#message p {
364
+	padding-bottom: 4px;
365
+}
366
+#texte-msg {
367
+	width:80%;
368
+	background-color: #C4E8D7;
369
+	margin-right: 5px;
370
+}
371
+button#add{
372
+    min-width:180px;
373
+    margin:auto;
374
+    padding:10px;
375
+    background: #b7a86b;
376
+    -moz-border-radius: 10px;
377
+    -webkit-border-radius: 10px;
378
+    -khtml-border-radius: 10px;
379
+    border-radius: 10px;
380
+    display:block;
381
+}
382
+
383
+thead{
384
+    font-weight: bold;
385
+}
386
+
387
+fieldset.fieldset {
388
+    border:1px solid green;
389
+    padding:30px;
390
+    margin:20px;
391
+}

BIN
static/cards/0C.png


BIN
static/cards/0F.png


BIN
static/cards/0P.png


BIN
static/cards/0T.png


BIN
static/cards/7C.png


BIN
static/cards/7F.png


BIN
static/cards/7P.png


BIN
static/cards/7T.png


BIN
static/cards/8C.png


BIN
static/cards/8F.png


BIN
static/cards/8P.png


BIN
static/cards/8T.png


BIN
static/cards/9C.png


BIN
static/cards/9F.png


BIN
static/cards/9P.png


BIN
static/cards/9T.png


BIN
static/cards/AC.png


BIN
static/cards/AF.png


BIN
static/cards/AP.png


BIN
static/cards/AT.png


BIN
static/cards/C.png


BIN
static/cards/DC.png


BIN
static/cards/DF.png


BIN
static/cards/DP.png


BIN
static/cards/DT.png


BIN
static/cards/F.png


BIN
static/cards/P.png


BIN
static/cards/RC.png


BIN
static/cards/RF.png


BIN
static/cards/RP.png


BIN
static/cards/RT.png


BIN
static/cards/T.png


BIN
static/cards/VC.png


BIN
static/cards/VF.png


BIN
static/cards/VP.png


BIN
static/cards/VT.png


BIN
static/croix.png


BIN
static/edit.png


BIN
static/fleche-bas.gif


BIN
static/fleche-haut.gif


+ 262 - 0
static/game_script.js

@@ -0,0 +1,262 @@
1
+var socket = null;
2
+var sendtext = function(){
3
+    var text = $('#texte-msg').val();
4
+    if (text.length > 1){
5
+        $('#texte-msg').val('');
6
+        socket.emit('text', {'text':text});
7
+    }
8
+};
9
+
10
+var choixcouleur = function(coul){
11
+    socket.emit('choose_color', {'atout':coul});
12
+}
13
+
14
+var jouer = function(carte, no){
15
+    socket.emit('play', {'card':carte});
16
+}
17
+var restart_jeu = function(){
18
+    socket.emit('restart', {});
19
+}
20
+
21
+
22
+$.fn.pressEnter = function (fnc) {
23
+    return this.each(function () {
24
+        $(this).keypress(function (ev) {
25
+            var keycode = (ev.keyCode ? ev.keyCode : ev.which);
26
+            if (keycode == '13') {
27
+                fnc.call(this, ev);
28
+            }
29
+        })
30
+    })
31
+}
32
+
33
+var set_points = function(){
34
+    $('#nous-tot').html( jeu['cumul'][jeu['nr']%2]     );
35
+    $('#nous-part').html(jeu['points'][jeu['nr']%2]    );
36
+    $('#eux-tot').html(  jeu['cumul'][(jeu['nr']+1)%2] );
37
+    $('#eux-part').html( jeu['points'][(jeu['nr']+1)%2]);
38
+    $('#nopart').html('Partie '+jeu['partie']);
39
+}
40
+
41
+var set_atout = function(){
42
+    if(jeu['atout'] == null || jeu['atout']==''){
43
+        var coulprop = jeu['played'][0];
44
+        $('#atout').html(`<img src="/static/cards/${coulprop}.png" width=100% />`);
45
+        if (jeu['turn']<-4 && (jeu['turn']+jeu['first_player']+8)%4 == jeu['nr']){
46
+            $('#prispar').html(`<input type="submit" onclick="javascript:choixcouleur('${coulprop[1]}')" value="Prendre" />`);
47
+            $('#preneur').html(`<input type="submit" onclick="javascript:choixcouleur(null)" value="Passer" />`);
48
+        }else if((jeu['turn']+jeu['first_player']+8)%4 == jeu['nr']){
49
+            $('#prispar').html(`<input type="submit" onclick="javascript:choixcouleur('P')" value="Pique" />
50
+                                <input type="submit" onclick="javascript:choixcouleur('C')" value="Coeur" />
51
+                                <br />
52
+                                <input type="submit" onclick="javascript:choixcouleur('T')" value="Trèfle" />
53
+                                <input type="submit" onclick="javascript:choixcouleur('F')" value="Carreau" />
54
+                `);
55
+            $('#preneur').html(`<input type="submit" onclick="javascript:choixcouleur(null)" value="Passer" />`);
56
+        }else{
57
+            $('#prispar').html(`Pris par`);
58
+            $('#preneur').html(`----`);
59
+        }
60
+    }else{
61
+        $('#atout').html(`<img src="/static/cards/${jeu['atout']}.png" width=100% />`);
62
+        $('#prispar').html(`Pris par`);
63
+        $('#preneur').html(`${jeu['playersinfo'][jeu['preneur']]}`);
64
+    }
65
+}
66
+
67
+jeu['monjeu'] = Array.from(jeu['cards']);
68
+var order = ['A', '0', 'R', 'D', 'V', '9', '8', '7'];
69
+var order_atout = ['V', '9', 'A', '0', 'R', 'D', '8', '7'];
70
+var set_jeu = function(){
71
+    var order_colors = ['P', 'C', 'T', 'F'];
72
+    if (jeu['atout'] == 'C'){
73
+        order_colors = ['C', 'T', 'F', 'P'];
74
+    }else if (jeu['atout'] == 'T'){
75
+        order_colors = ['T', 'C', 'P', 'F'];
76
+    }else if (jeu['atout'] == 'F'){
77
+        order_colors = ['F', 'T', 'C', 'P'];
78
+    }
79
+    jeu['monjeu'].sort(function(a,b){
80
+      if (a[1]==jeu['atout']){
81
+          x = order_atout.indexOf(a[0]);
82
+      }else{
83
+          x = order.indexOf(a[0]);
84
+      }
85
+      if (b[1]==jeu['atout']){
86
+          y = order_atout.indexOf(b[0]);
87
+      }else{
88
+          y = order.indexOf(b[0]);
89
+      }
90
+      return x+15*order_colors.indexOf(a[1]) > y+15*order_colors.indexOf(b[1]) ? 1 : -1;
91
+    });
92
+    console.log(jeu['monjeu']);
93
+    
94
+    for(i=0;i<jeu['monjeu'].length;i++){
95
+        if (jeu['monjeu'][i]==null){
96
+            $('#carte-'+i).html('');
97
+        }else{
98
+            $('#carte-'+i).html(`<a href="javascript:jouer('${jeu['monjeu'][i]}', ${i})"><img src="/static/cards/${jeu['monjeu'][i]}.png" width=100% /></a>`);
99
+        }
100
+    }
101
+}
102
+
103
+var set_cartes = function(){
104
+    first_player = jeu['first_player']
105
+    if (jeu['turn']%4==0 && 'lastfirstplayer' in jeu){
106
+        first_player = jeu['lastfirstplayer']
107
+    }
108
+    if (jeu['preneur'] == null){
109
+        $('#jeu-1').html('');
110
+        $('#jeu-2').html('');
111
+        $('#jeu-3').html('');
112
+        $('#jeu-4').html('');
113
+    }else{
114
+        for(i=first_player;i<first_player+4;i++){
115
+            j = i-first_player
116
+            k = i%4
117
+            if (jeu['played'].length > j && jeu['played'][j]!= ''){
118
+                $('#jeu-'+ k).html(`<img src="/static/cards/${jeu['played'][j]}.png" width=100% />`);
119
+            }else{
120
+                $('#jeu-'+ k).html('');
121
+            }
122
+        }
123
+    }
124
+}
125
+
126
+var set_derpli = function(){
127
+    if(jeu['last_played'].length >0){
128
+        for(i=0;i<4;i++){
129
+            $('#der-pli-'+i).html(`<img src="/static/cards/${jeu['last_played'][i]}.png" width=100% />`);
130
+        }
131
+    }
132
+}
133
+
134
+var set_players = function(){
135
+    for(i=0;i<4;i++){
136
+        $('#joueur-'+i).html(`${jeu['playersinfo'][jeu['players'][i]]}`);
137
+    }
138
+}
139
+
140
+var set_current_player = function(){
141
+    for(i=0;i<4;i++){
142
+        $('#joueur-'+i).css('font-weight', 'normal');
143
+    }
144
+    i = (jeu['turn']+jeu['first_player']+8)%4;
145
+    $('#joueur-'+i).css('font-weight', 'bold');
146
+    if((jeu['turn']+jeu['first_player']+8)%4 == jeu['nr']){
147
+        $('#a-vous').css('visibility', 'visible');
148
+    }else{
149
+        $('#a-vous').css('visibility', 'hidden');
150
+    }
151
+
152
+    if(jeu['turn']==32 && jeu['admin']==jeu['players'][jeu['nr']]){
153
+        $('#suivant').show();
154
+    }
155
+}
156
+
157
+
158
+$(document).ready(function() {
159
+socket = io(window.location.pathname);
160
+$('#suivant').hide()
161
+set_points();
162
+set_atout();
163
+set_jeu();
164
+set_cartes();
165
+set_derpli();
166
+set_players();
167
+set_current_player();
168
+
169
+for (e in jeu){
170
+        $('#jeu').append(`${e} : ${jeu[e]} <br />`)
171
+}
172
+if((jeu['turn']+jeu['first_player'] + 8)%4 == jeu['nr']){
173
+    $('#a-vous').css('visibility', 'visible');
174
+}
175
+
176
+
177
+socket.on('connect', function() {
178
+    socket.emit('join', {data: 'I\'m connected!'});
179
+});
180
+
181
+socket.on('text', function(data) {
182
+    textr = data['text'];
183
+    name = data['name'];
184
+    $('#chat').prepend(`<b>${name}</b> : ${textr}<br />`);
185
+});
186
+
187
+socket.on('restart', function(data) {
188
+    for(d in data){
189
+        jeu[d] = data[d]
190
+    }
191
+    set_points();
192
+    set_atout();
193
+    set_cartes();
194
+    set_derpli();
195
+    set_players();
196
+    set_current_player();
197
+    socket.emit('monjeu', {});
198
+});
199
+
200
+socket.on('choose_color', function(data){
201
+    console.log(data)
202
+    jeu['turn'] = data['turn']
203
+    jeu['preneur'] = data['preneur']
204
+    jeu['atout'] = data['atout']
205
+    set_atout();
206
+    set_current_player();
207
+    if(jeu['turn']>=0){
208
+        socket.emit('monjeu', {});
209
+    }
210
+});
211
+
212
+socket.on('monjeu', function(data){
213
+    jeu['cards'] = data['cards'];
214
+    jeu['monjeu'] = Array.from(data['cards']);
215
+    set_jeu();
216
+});
217
+
218
+socket.on('play', function(data){
219
+    console.log(data)
220
+    jeu['turn'] = data['turn'];
221
+    jeu['lastfirstplayer'] = jeu['first_player']
222
+    jeu['first_player'] = data['first_player'];
223
+    jeu['last_played'] = data['last_played'];
224
+    jeu['points'] = data['points'];
225
+    if(data['played'] == null || data['played'].length == 0){
226
+        jeu['played'] = data['last_played'];
227
+        console.log(jeu['played'])
228
+        set_cartes();
229
+        set_derpli();
230
+        set_points();
231
+    }
232
+    else{
233
+        jeu['played'] = data['played'];
234
+        console.log(jeu['played'])
235
+        set_cartes();
236
+        set_points();
237
+    }
238
+    var first_player = jeu['first_player'];
239
+    if (jeu['turn']%4==0 && 'lastfirstplayer' in jeu){
240
+        first_player = jeu['lastfirstplayer']
241
+    }
242
+    if((jeu['turn']+first_player+7)%4 == jeu['nr']){
243
+        console.log("Je viens de jouer")
244
+        i = jeu['monjeu'].indexOf(jeu['played'][jeu['played'].length-1]);
245
+        if (jeu['played'].length>0 && i >=0){
246
+            console.log(i)
247
+            jeu['monjeu'][i] = null;
248
+            $('#carte-'+i).html('')
249
+        }
250
+
251
+    }
252
+    set_current_player();
253
+});
254
+
255
+
256
+$('#texte-msg').pressEnter(function(e){
257
+    sendtext();
258
+});
259
+
260
+
261
+
262
+});

BIN
static/logo.gif


BIN
static/ok.png


BIN
static/plus.png


+ 28 - 0
templates/admin_game_add.html

@@ -0,0 +1,28 @@
1
+{% extends "base.html" %}
2
+
3
+{% block title %} Créer jeu {% endblock %}
4
+
5
+{% block titre %} Créer un jeu {% endblock %}
6
+
7
+{% block contenu %}
8
+<div id="zone-de-contenu">
9
+        <div style="width:100%;margin:20px;"> </div>
10
+
11
+      <fieldset class="fieldset"><legend style="font-size:25px;">Nom du jeu</legend>
12
+          {% for message in get_flashed_messages() %}
13
+            <div class="alert alert-warning">
14
+                <button type="button" class="close" data-dismiss="alert">&times;</button>
15
+                {{ message }}
16
+            </div>
17
+          {% endfor %}
18
+        <form action="" method="POST" accept-charset="utf-8">
19
+            Nom du jeu : <input type="text" name="name" id="name" placeholder="Nom du jeu"/><br />
20
+            Maitre du jeu : <input type="text" name="admin" placeholder="Login du maitre du jeu" /><br />
21
+            <input id="submit" type="submit" value="Créer">
22
+        </form>
23
+      </fieldset>
24
+</div>
25
+
26
+{% endblock %}
27
+
28
+

+ 27 - 0
templates/admin_games.html

@@ -0,0 +1,27 @@
1
+{% extends "base.html" %}
2
+
3
+{% block title %} Administration {% endblock %}
4
+
5
+{% block titre %}Administration : jeux {% endblock %}
6
+
7
+{% block contenu %}
8
+<div id="zone-de-contenu">
9
+    <fieldset class="fieldset" ><legend style="font-size:25px;">Ajouter un utilisateur</legend>
10
+            <button id="add" onclick="javascript:go('/admin/games/new')">
11
+            <img src="/static/plus.png" style="float:left;" alt=''/><div style="float:right; margin:5px;">Nouveau jeu</div>
12
+            </button>
13
+  </fieldset>
14
+  <fieldset class="fieldset"><legend style="font-size:25px;">Liste des jeux</legend>
15
+    <table width=100%>
16
+        <thead>
17
+            <tr><td>Id</td><td>Name</td><td>Partie</td><td>Joueurs</td><td>Status</td><td>Actions</td></tr>
18
+        </thead>
19
+        {% for g in games %}
20
+        <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>
21
+            <td>{% if g.start %} Started {% elif g.fixplayers %}Players set{% else %}Join{% endif %}</td>
22
+            <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>
23
+        {% endfor %}
24
+    </table>
25
+  </fieldset>
26
+</div>
27
+{% endblock %}

+ 17 - 0
templates/admin_user_del.html

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

+ 40 - 0
templates/admin_user_edit.html

@@ -0,0 +1,40 @@
1
+{% extends "base.html" %}
2
+
3
+{% block title %} Administration {% endblock %}
4
+
5
+{% block titre %} Administration : Éditer un utilisateur {% endblock %}
6
+
7
+{% block contenu %}
8
+<div id="zone-de-contenu">
9
+    <form method="POST" action="">
10
+        <div style="width:100%;margin:20px;"> </div>
11
+
12
+      <fieldset class="fieldset"><legend style="font-size:25px;">Utilisateur</legend>
13
+          {% for message in get_flashed_messages() %}
14
+            <div class="alert alert-warning">
15
+                <button type="button" class="close" data-dismiss="alert">&times;</button>
16
+                {{ message }}
17
+            </div>
18
+          {% endfor %}
19
+          <table width=100%>
20
+              <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>
21
+              <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>
22
+              <tr><td> Mot de passe : </td><td> <input type="password" name="password"  placeholder="Mot de passe"/></td></tr>
23
+              <tr><td> Droits : </td><td> <select name="level">
24
+                          {% for e in levels_users %}
25
+                          {% if useredit is none %}
26
+                              <option value="{{ levels_users[e] }}" {% if e == 'Simple user' %}selected{% endif %}>{{ e }}</option>
27
+                          {% else %}
28
+                              <option value="{{ levels_users[e] }}" {% if useredit.level == levels_users[e] %}selected{% endif %}>{{ e }}</option>
29
+                          {% endif %}
30
+                          {% endfor %}
31
+                      </select>
32
+                  </td></tr>
33
+              <tr><td>&nbsp;</td> <td><input id="submit" type="submit" value="&nbsp;Entrer&nbsp;"></td></tr>
34
+           </table>
35
+      </fieldset>
36
+    </form>
37
+</div>
38
+
39
+{% endblock %}
40
+

+ 25 - 0
templates/admin_users.html

@@ -0,0 +1,25 @@
1
+{% extends "base.html" %}
2
+
3
+{% block title %} Administration {% endblock %}
4
+
5
+{% block titre %}Administration : utilisateurs {% endblock %}
6
+
7
+{% block contenu %}
8
+<div id="zone-de-contenu">
9
+    <fieldset class="fieldset" ><legend style="font-size:25px;">Ajouter un utilisateur</legend>
10
+            <button id="add" onclick="javascript:go('/admin/users/new')">
11
+            <img src="/static/plus.png" style="float:left;" alt=''/><div style="float:right; margin:5px;">Nouvel utilisateur</div>
12
+            </button>
13
+  </fieldset>
14
+  <fieldset class="fieldset"><legend style="font-size:25px;">Liste des utilisateurs</legend>
15
+    <table width=100%>
16
+        <thead>
17
+            <tr><td>Login</td><td>Name</td><td>Level</td><td>Last login</td><td>Actions</td></tr>
18
+        </thead>
19
+        {% for u in users %}
20
+        <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>
21
+        {% endfor %}
22
+    </table>
23
+  </fieldset>
24
+</div>
25
+{% endblock %}

+ 52 - 0
templates/base.html

@@ -0,0 +1,52 @@
1
+<html>
2
+    <head>
3
+        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
4
+            {% include ['head-perso.html', 'head.html'] %}
5
+            {% block head %}
6
+            <title>{% block title %}{% endblock %} - Belote</title>
7
+            {% endblock %}
8
+    </head>
9
+    <body>
10
+        <div id="conteneur">
11
+            <!--     ENTETE       -->
12
+            <div id="entete" class="clearfix-head">
13
+                <div id="logo">
14
+                    <img src="/static/logo.gif" />
15
+                </div><!-- logo -->
16
+                <div id="titre">
17
+                    {% block titre %} {% endblock %}
18
+                </div><!-- titre -->
19
+                <div id="username">
20
+                    {% if not user == None %}
21
+                        {{ user.name }}
22
+                    {% else %}
23
+                        S'identifier
24
+                    {% endif %}
25
+                    <img id="username-fleche" src="/static/fleche-bas.gif"/>
26
+                </div><!-- usernamev -->
27
+                <div id="username-items">
28
+                    {% if not user == None %}
29
+                    <div id="username-item1" class="menu-item" onclick="javascript:go('/games')">Liste des jeux</div>
30
+                    <div id="username-item2" class="menu-item" onclick="javascript:go('/password')">Changer mon mot de passe</div>
31
+                    <div id="username-item3" class="menu-item" onclick="javascript:go('/logout')">Se déconnecter</div>
32
+                    {% if user.level > 9 %}
33
+                    <div id="username-item4" class="menu-item" onclick="javascript:go('/admin/games')">Admin des jeux</div>
34
+                    <div id="username-item5" class="menu-item" onclick="javascript:go('/admin/users')">Admin des utilisateurs</div>
35
+                    {% endif %}
36
+                    {% else %}
37
+                    <div id="username-item1" class="menu-item" onclick="javascript:go('/login')">S'identifier</div>
38
+                    {% endif %}
39
+                </div><!-- username-items-->
40
+                </div><!-- entete -->
41
+
42
+                <!-- CONTENU -->
43
+                {% block contenu %}
44
+                {% endblock %}
45
+                <!-- / CONTENU -->
46
+
47
+        </div>
48
+        {% block script %} {% endblock %}
49
+    </body>
50
+</html>
51
+
52
+

+ 44 - 0
templates/belote.html

@@ -0,0 +1,44 @@
1
+<!-- 
2
+La Belote 
3
+-->
4
+<html>
5
+<head>
6
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
7
+<meta name="viewport" content="width=device-width,initial-scale=1.0">
8
+<meta http-equiv="cache-control" content="no-cache" />
9
+<title>Belote</title>
10
+<!-- <link rel='stylesheet' type='text/css'  href='./belote-reset.css' />-->
11
+<link rel='stylesheet' type='text/css'  href='./belote-presentation.css' />
12
+<script src="./jquery-3.5.0.js"></script>
13
+<script src="./belote-script.js"></script>
14
+</head>
15
+<!----------------------
16
+     BODY 
17
+----------------------->
18
+<body>
19
+<div id="conteneur">
20
+<!----------------------
21
+     ENTETE 
22
+----------------------->
23
+<div id="entete" class="clearfix-head">
24
+<div id="logo">
25
+<img src="logo.gif" />
26
+</div><!-- logo -->
27
+<div id="titre">
28
+TITRE
29
+</div><!-- titre -->
30
+<div id="username">
31
+username 
32
+<img id="username-fleche" src="./fleche-bas.gif"/>
33
+</div><!-- usernamev -->
34
+<div id="username-items">
35
+<div id="username-item1" class="menu-item">- item 1</div>
36
+<div id="username-item2" class="menu-item">- item 2</div>
37
+</div><!-- username-items-->
38
+</div><!-- entete -->
39
+<!----------------------
40
+     ZONE-DE-JEU 
41
+----------------------->
42
+/div><!-- conteneur -->
43
+</body> 
44
+</html>

+ 21 - 0
templates/game.html

@@ -0,0 +1,21 @@
1
+{% extends "base.html" %}
2
+
3
+{% block title %} Jeu {% endblock %}
4
+
5
+{% block titre %} Jeu {{ game.name }} {% endblock %}
6
+
7
+{% block head %}
8
+{% include ['scripts-perso.html', 'scripts.html'] %}
9
+{% endblock %}
10
+
11
+{% block contenu %}
12
+    {% include "game_zone_jeu.html" %}
13
+{% endblock %}
14
+
15
+{% block script %}
16
+<script type="text/javascript" charset="utf-8">
17
+    var None = null;
18
+    var jeu = {{ game.serialize_state(user)|safe }};
19
+</script>
20
+<script src="/static/game_script.js" charset="utf-8"></script>
21
+{% endblock %}

+ 21 - 0
templates/game_add.html

@@ -0,0 +1,21 @@
1
+{% extends "base.html" %}
2
+
3
+{% block title %} Créer jeu {% endblock %}
4
+
5
+{% block titre %} Créer un jeu {% endblock %}
6
+
7
+{% block contenu %}
8
+<div id="zone-de-contenu">
9
+        <div style="width:100%;margin:20px;"> </div>
10
+
11
+      <fieldset class="fieldset"><legend style="font-size:25px;">Nom du jeu</legend>
12
+        <form action="" method="POST" accept-charset="utf-8">
13
+            Nom du jeu : <input type="text" name="name" id="name" />
14
+            <input id="submit" type="submit" value="Créer">
15
+        </form>
16
+      </fieldset>
17
+</div>
18
+
19
+{% endblock %}
20
+
21
+

+ 17 - 0
templates/game_del.html

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

+ 166 - 0
templates/game_join.html

@@ -0,0 +1,166 @@
1
+{% extends "base.html" %}
2
+
3
+{% block title %} Jeu {% endblock %}
4
+
5
+{% block titre %} Jeu {{game.name}} {% endblock %}
6
+
7
+{% block head %}
8
+{% include ['scripts-perso.html', 'scripts.html'] %}
9
+{% endblock %}
10
+
11
+{% block contenu %}
12
+<div id="zone-de-contenu">
13
+        <div style="width:100%;margin:20px;"> </div>
14
+        
15
+      <fieldset class="fieldset"><legend style="font-size:25px;">Liste des joueurs</legend>
16
+        <ul id="players">
17
+        {% for p in game.get_players() %}
18
+        {% if p['username'] == admin %}
19
+            <li id="player_{{ p['username'] }}"> {{ p['name'] }} ({{ p['username'] }}) (admin)</li>
20
+        {% else %}
21
+            {% if p['username'] == user.login %}
22
+                <li id="player_{{ p['username'] }}"> {{ p['name'] }} ({{ p['username'] }}) <a href="javascript:leave()">(quitter le jeu)</a></li>
23
+            {% elif user.login == admin %}
24
+                <li id="player_{{ p['username'] }}"> {{ p['name'] }} ({{ p['username'] }}) <a href="javascript:ban('{{ p['username'] }}')">(bannir)</a></li>
25
+            {% else %}
26
+                <li id="player_{{ p['username'] }}"> {{ p['name'] }} ({{ p['username'] }})</li>
27
+            {% endif %}
28
+        {% endif %}
29
+        {% endfor %}
30
+        </ul>
31
+      </fieldset>
32
+        {% if user.login == admin %}
33
+      <fieldset class="fieldset"><legend style="font-size:25px;">Gestion des participants</legend>
34
+            <form action="javascript:addplayer()">
35
+            Ajouter par nom d'utilisateur : 
36
+            <input type="text" id="addplayer" />
37
+            <input type="submit" value="Ajouter">
38
+            </form>
39
+
40
+            <br />
41
+            Vous pouvez aussi donner l'adresse du jeu (lire dans la barre d'adresse).
42
+        </fieldset>
43
+       <fieldset class="fieldset" id="gostart"><legend style="font-size:25px;">Démarrer le jeu</legend>
44
+            <a href="javascript:gostart()">Commencer</a>
45
+      </fieldset>
46
+        {% else %}
47
+      <fieldset class="fieldset"><legend style="font-size:25px;">Info</legend>
48
+            <p>Veuillez attendre que le maitre du jeu lance la partie...</p>
49
+            <p>Rechargez la page si cela traine trop.</p>
50
+        </fieldset>
51
+        {% endif %}
52
+
53
+{% endblock %}
54
+
55
+{% block script %}
56
+
57
+<script type="text/javascript" charset="utf-8">
58
+var players = [
59
+    {% for p in game.get_players() %}
60
+    '{{ p['username'] }}',
61
+    {% endfor %}
62
+];
63
+
64
+{% if game.fixplayers %}
65
+var fix = true;
66
+{% else %}
67
+var fix = false;
68
+{% endif %}
69
+
70
+{% if user.login == admin %}
71
+    if (players.length < 4){
72
+        $('#gostart').hide();
73
+    }
74
+    else{
75
+        $('#add_player').hide();
76
+    }
77
+{% endif %}
78
+
79
+var socket = io(window.location.pathname);
80
+
81
+socket.on('connect', function() {
82
+    socket.emit('join', {data: 'I\'m connected!'});
83
+});
84
+
85
+socket.on('join', function(data){
86
+    console.log('Join received', data['username'], data['name']);
87
+    add_u(data['username'], data['name']);
88
+    {% if user.login == admin %}
89
+        if (players.length == 4){
90
+            $('#gostart').show();
91
+            $('#add_player').hide();
92
+        }
93
+    {% endif %}
94
+});
95
+
96
+var add_u = function(username, name){
97
+    if(players.indexOf(username)<0){
98
+        players.push(username);
99
+        html = `<li id="player_${username}"> ${name} (${username})`
100
+        if(username == '{{ admin }}'){html+= ` (admin)`;}
101
+        else if(username == '{{ user.login }}'){html+= ` <a href="javascript:leave()">(quitter le jeu)</a>`;}
102
+        {% if user.login == admin -%}
103
+        else{html+= ` <a href="javascript:ban('${username}')">(bannir)</a>`;}
104
+        {%- endif %}
105
+        html += `</li>`
106
+        $('#players').append(html)
107
+        fix_f();
108
+    }
109
+}
110
+
111
+socket.on('leave', function(data){
112
+    console.log('Leave received', data['username']);
113
+    username = data['username'];
114
+    players.splice(players.indexOf(username), 1);
115
+    $('#player_' + username).remove();
116
+
117
+    {% if user.login == admin %}
118
+        $('#gostart').hide();
119
+        $('#add_player').show();
120
+    {% endif %}
121
+});
122
+
123
+socket.on('fixplayers', function(data){
124
+    fix = True;
125
+    fix_f();
126
+});
127
+
128
+var fix_f=function(){
129
+    if(fix){
130
+        $('#players li a').remove();
131
+    {% if user.login == admin %}
132
+        $('#add_player').hide();
133
+    {% endif %}
134
+    }
135
+}
136
+fix_f();
137
+
138
+socket.on('start', function(data){
139
+    document.location.href="/game/{{ game.id }}";
140
+});
141
+
142
+var leave = function(){
143
+    socket.emit('leave', {});
144
+    document.location.href ="/games";
145
+}
146
+
147
+{% if user.login == admin %}
148
+var ban = function(username){
149
+    socket.emit('ban', {'username': username});
150
+}
151
+
152
+var gostart = function(){
153
+    socket.emit('fixplayers', {});
154
+    document.location.href="/game/{{ game.id }}/start";
155
+}
156
+
157
+var addplayer = function(){
158
+    username = $('#addplayer').val();
159
+    if(username !=""){
160
+        $('#addplayer').val('');
161
+        socket.emit('add_player',{'username':username})
162
+    }
163
+}
164
+{% endif %}
165
+</script>
166
+{% endblock %}

+ 88 - 0
templates/game_start.html

@@ -0,0 +1,88 @@
1
+{% extends "base.html" %}
2
+
3
+{% block title %} Jeu {% endblock %}
4
+
5
+{% block titre %} Jeu {{ game.name }} {% endblock %}
6
+
7
+{% block head %}
8
+{% include ['scripts-perso.html', 'scripts.html'] %}
9
+{% endblock %}
10
+
11
+{% block contenu %}
12
+<div id="zone-de-contenu">
13
+      <fieldset class="fieldset"><legend style="font-size:25px;">Liste des joueurs</legend>
14
+        <p>Faites les équipes :</p>
15
+        <ul id="players">
16
+        </ul>
17
+      </fieldset>
18
+      <fieldset class="fieldset"><legend style="font-size:25px;">Commencer</legend>
19
+            <a href="javascript:gostart()">Commencer</a>
20
+      </fieldset>
21
+</div>
22
+
23
+{% endblock %}
24
+
25
+{% block script %}
26
+<script type="text/javascript" charset="utf-8">
27
+var players_order = [
28
+    {% for p in game.get_players() %}
29
+    '{{ p['username'] }}',
30
+    {% endfor %}
31
+];
32
+var players = {
33
+    {% for p in game.get_players() %}
34
+    '{{ p['username'] }}': '{{ p['name'] }}',
35
+    {% endfor %}
36
+};
37
+
38
+var put_players = function(){
39
+    var html = '';
40
+    for(i=0;i<players_order.length;i++){
41
+        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>`;
42
+    }
43
+    $('#players').html(html);
44
+}
45
+
46
+put_players();
47
+
48
+var up=function(i){
49
+    t = players_order[i];
50
+    if(i==0){
51
+        n=players_order.length-1;
52
+    }else{
53
+        n = i-1;
54
+    }
55
+    players_order[i] = players_order[n]
56
+    players_order[n] = t
57
+    put_players();
58
+}
59
+
60
+var down = function(i){
61
+    t = players_order[i];
62
+    if(i==players_order.length-1){
63
+        n=0;
64
+    }else{
65
+        n = i+1;
66
+    }
67
+    players_order[i] = players_order[n]
68
+    players_order[n] = t
69
+    put_players();
70
+}
71
+
72
+var socket = io('/game/{{ game.id }}/join');
73
+
74
+socket.on('connect', function() {
75
+});
76
+
77
+
78
+socket.on('start', function(data){
79
+    document.location.href="/game/{{ game.id }}";
80
+});
81
+
82
+
83
+var gostart = function(){
84
+    socket.emit('start', {'order':players_order});
85
+    document.location.href="/game/{{ game.id }}";
86
+}
87
+</script>
88
+{% endblock %}

+ 95 - 0
templates/game_zone_jeu.html

@@ -0,0 +1,95 @@
1
+<div id="zone-de-jeu">
2
+<div id="plateau">
3
+<div id="joueur-0" class="joueur">joueur 0</div><!-- joueur-0 -->
4
+<div id="joueur-1" class="joueur">joueur 1</div><!-- joueur-1 -->
5
+<div id="joueur-2" class="joueur">joueur 2</div><!-- joueur-2 -->
6
+<div id="joueur-3" class="joueur">joueur 3</div><!-- joueur-3 -->
7
+<div id="jeu-0" class="carte"></div><!-- jeu-0 -->
8
+<div id="jeu-1" class="carte"></div><!-- jeu-1 -->
9
+<div id="jeu-2" class="carte"></div><!-- jeu-2 -->
10
+<div id="jeu-3" class="carte"></div><!-- jeu-3 -->
11
+<form id="suivant">
12
+<input type="submit" name="suivant" value="Recommencer" onclick="javascript:restart_jeu()"> 
13
+</form><!-- suivant -->
14
+</div><!-- plateau -->
15
+<div id="ov">
16
+<table id="table-atout">
17
+<tr>
18
+<td>Atout</td>
19
+<td><div id="atout" class="carte"></div></td>
20
+</tr>
21
+<tr>
22
+<td id="prispar">Pris par </td>
23
+<td id="preneur">----</td>
24
+</tr>
25
+</table>
26
+<div id="choix">
27
+<div id="dernier-pli">
28
+<p class="item-choix">Dernier pli <img id="dernier-pli-fleche" src="/static/fleche-bas.gif" /></p>
29
+<div id="dernier-pli-valeur" class="item-choix-valeur clearfix">
30
+<div id="der-pli-0" class="carte carte-dernier-pli"></div>
31
+<div id="der-pli-1" class="carte carte-dernier-pli"></div>
32
+<div id="der-pli-2" class="carte carte-dernier-pli"></div>
33
+<div id="der-pli-3" class="carte carte-dernier-pli"></div>
34
+</div>
35
+</div><!-- dernier-pli -->
36
+<div id="options">
37
+<p class="item-choix">Options <img id="options-fleche" src="/static/fleche-bas.gif" /></p>
38
+<!--<div id="options-valeur" class="item-choix-valeur">Texte</div>-->
39
+</div><!-- options -->
40
+</div><!-- choix -->
41
+</div><!-- ov -->
42
+<div id="jeu-en-main" class="clearfix">
43
+<div id="a-vous">A vous de jouer !
44
+</div>
45
+<div id="carte-0" class="carte carte-en-main">
46
+</div>
47
+<div id="carte-1" class="carte carte-en-main">
48
+</div>
49
+<div id="carte-2" class="carte carte-en-main">
50
+</div>
51
+<div id="carte-3" class="carte carte-en-main">
52
+</div>
53
+<div id="carte-4" class="carte carte-en-main">
54
+</div>
55
+<div id="carte-5" class="carte carte-en-main">
56
+</div>
57
+<div id="carte-6" class="carte carte-en-main">
58
+</div>
59
+<div id="carte-7" class="carte carte-en-main">
60
+</div>
61
+</div><!-- jeu-en-main -->
62
+<div id="points">
63
+<p id="entete-points">Points
64
+</p>
65
+<table>
66
+<tr>
67
+<td></td>
68
+<td>Total</td>
69
+<td id="nopart">Partie</td>
70
+</tr>
71
+<tr>
72
+<td>Nous</td>
73
+<td id="nous-tot">0</td>
74
+<td id="nous-part">0</td>
75
+</tr>
76
+<tr>
77
+<td>Eux</td>
78
+<td id="eux-tot">0</td>
79
+<td id="eux-part">0</td>
80
+</tr>
81
+</table>
82
+</div><!-- points -->
83
+</div><!-- zone-de-jeu -->
84
+<!------------------------
85
+     ZONE-DE-CONVERSATION 
86
+------------------------->
87
+<div id="zone-de-conversation">
88
+<div id="chat">
89
+</div><!-- chat -->
90
+<div id="message">
91
+<p>Message</p>
92
+<input id="texte-msg" type="text" name="msg">
93
+<input type="submit" name="OK" value="OK" onclick="javascript:sendtext()"> 
94
+</div><!-- zone-message -->
95
+</div><!-- zone-de-conversation -->

+ 43 - 0
templates/games.html

@@ -0,0 +1,43 @@
1
+{% extends "base.html" %}
2
+
3
+{% block title %} Jeux {% endblock %}
4
+
5
+{% block titre %} Liste des jeux {% endblock %}
6
+
7
+{% block contenu %}
8
+<div id="zone-de-contenu">
9
+        {% if cancreategames %}
10
+        <fieldset class="fieldset" ><legend style="font-size:25px;">Ajouter un jeu</legend>
11
+            <button id="add" onclick="javascript:go('/add_game')">
12
+            <img src="/static/plus.png" style="float:left;" alt=''/><div style="float:right; margin:5px;">Créer un nouveau jeu</div>
13
+            </button>
14
+        </fieldset>
15
+        {% endif %}
16
+        {% if mesjeux|length > 0 %}
17
+        <fieldset class="fieldset" ><legend style="font-size:25px;">Mes jeux</legend>
18
+            <table class="tablelist">
19
+            {% for g in mesjeux %}
20
+                <tr>
21
+                    <td width=80%><a href="/game/{{g.id}}"> {{ g.name }} </a></td>
22
+                    <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>
23
+                </tr>
24
+            {% endfor %}
25
+            </table>
26
+        </fieldset>
27
+        {% endif %}
28
+        {% if joinable|length > 0 %}
29
+        <fieldset class="fieldset" ><legend style="font-size:25px;">Autres jeux</legend>
30
+            <table class="tablelist">
31
+            {% for g in joinable %}
32
+                <tr><td width=80%><a href="/game/{{g.id}}"> {{ g.name }} </a></td>
33
+                    <td width=20%><a href="/game/{{g.id}}"><img src="/static/ok.png" alt="Supprimer"/></a> </td>
34
+                </tr>
35
+                </li>
36
+            {% endfor %}
37
+            </table>
38
+        </fieldset>
39
+        {% endif %}
40
+</div>
41
+
42
+{% endblock %}
43
+

+ 5 - 0
templates/head.html

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

+ 12 - 0
templates/index.html

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

+ 29 - 0
templates/login.html