From 8d61fc4ab7373b4a576f3f9108cdf7768ae27096 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Tue, 29 Jan 2019 17:40:03 +0100
Subject: [PATCH] Code reorganization

---
 .../javascripts/components/variantSummary.js  | 30 --------
 client/client_OLD/javascripts/utils/array.js  | 37 ----------
 client/client_OLD/javascripts/variant.js      | 70 -------------------
 client/client_OLD/views/app.pug               | 51 --------------
 client/src/App.vue                            |  5 +-
 .../javascripts => src}/base_rules.js         | 18 ++---
 client/src/components/Settings.vue            | 14 ++--
 .../javascripts => src}/components/board.js   |  6 +-
 .../components/challengeList.js               |  0
 .../javascripts => src}/components/chat.js    |  0
 .../javascripts => src}/components/game.js    |  0
 .../components/gameList.js                    |  0
 .../components/moveList.js                    |  0
 .../components/problemSummary.js              |  0
 .../components/upsertUser.js                  |  0
 .../data/challengeCheck.js                    |  0
 .../javascripts => src}/data/nbPlayers.js     |  0
 .../javascripts => src}/data/userCheck.js     |  0
 client/src/main.js                            | 22 +++++-
 .../javascripts => src}/playCompMove.js       |  0
 client/src/router.js                          |  1 +
 .../stylesheets/_users.sass                   |  0
 .../stylesheets/index.sass                    |  0
 .../stylesheets/layout.sass                   |  0
 .../stylesheets/variant.sass                  |  0
 client/src/utils/array.js                     | 24 +++++++
 .../javascripts => src}/utils/datetime.js     |  4 +-
 client/src/utils/language.js                  | 31 --------
 client/src/utils/misc.js                      | 41 +++++++++--
 .../javascripts => src}/utils/printDiagram.js |  2 +-
 .../javascripts => src}/utils/squareId.js     |  4 +-
 .../javascripts => src}/utils/storage.js      |  0
 .../javascripts => src}/variants/Alice.js     |  0
 .../javascripts => src}/variants/Antiking.js  | 14 ++--
 .../Atomic.js => src/variants/Atomic_OLD.js}  |  0
 .../javascripts => src}/variants/Baroque.js   | 16 ++---
 .../javascripts => src}/variants/Berolina.js  |  0
 .../javascripts => src}/variants/Checkered.js |  4 +-
 .../javascripts => src}/variants/Chess960.js  |  0
 .../variants/Crazyhouse.js                    |  0
 .../javascripts => src}/variants/Dark.js      |  2 +-
 .../variants/Extinction.js                    |  0
 .../javascripts => src}/variants/Grand.js     | 18 ++---
 .../javascripts => src}/variants/Losers.js    | 14 ++--
 .../javascripts => src}/variants/Magnetic.js  |  0
 .../javascripts => src}/variants/Marseille.js |  2 +-
 .../javascripts => src}/variants/Switching.js |  0
 .../variants/Upsidedown.js                    | 12 ++--
 .../variants/Wildebeest.js                    | 14 ++--
 .../javascripts => src}/variants/Zen.js       |  0
 client/src/views/About.vue                    |  5 --
 client/src/views/Game.vue                     | 12 ++++
 .../components/room.js => src/views/Hall.vue} | 18 +++++
 client/src/views/Home.vue                     | 31 ++++++++
 .../tabGames.js => src/views/MyGames.vue}     |  5 ++
 .../problems.js => src/views/Problems.vue}    | 22 ++++++
 client/src/views/Test.vue                     | 22 ------
 .../views/correspondance_merge_hall.js}       |  0
 .../components => src/views}/rules.js         |  0
 client/src/{modals => }/welcome/en.pug        |  0
 client/src/{modals => }/welcome/es.pug        |  0
 client/src/{modals => }/welcome/fr.pug        |  0
 62 files changed, 244 insertions(+), 327 deletions(-)
 delete mode 100644 client/client_OLD/javascripts/components/variantSummary.js
 delete mode 100644 client/client_OLD/javascripts/utils/array.js
 delete mode 100644 client/client_OLD/javascripts/variant.js
 delete mode 100644 client/client_OLD/views/app.pug
 rename client/{client_OLD/javascripts => src}/base_rules.js (98%)
 rename client/{client_OLD/javascripts => src}/components/board.js (98%)
 rename client/{client_OLD/javascripts => src}/components/challengeList.js (100%)
 rename client/{client_OLD/javascripts => src}/components/chat.js (100%)
 rename client/{client_OLD/javascripts => src}/components/game.js (100%)
 rename client/{client_OLD/javascripts => src}/components/gameList.js (100%)
 rename client/{client_OLD/javascripts => src}/components/moveList.js (100%)
 rename client/{client_OLD/javascripts => src}/components/problemSummary.js (100%)
 rename client/{client_OLD/javascripts => src}/components/upsertUser.js (100%)
 rename client/{client_OLD/javascripts => src}/data/challengeCheck.js (100%)
 rename client/{client_OLD/javascripts => src}/data/nbPlayers.js (100%)
 rename client/{client_OLD/javascripts => src}/data/userCheck.js (100%)
 rename client/{client_OLD/javascripts => src}/playCompMove.js (100%)
 rename client/{client_OLD => src}/stylesheets/_users.sass (100%)
 rename client/{client_OLD => src}/stylesheets/index.sass (100%)
 rename client/{client_OLD => src}/stylesheets/layout.sass (100%)
 rename client/{client_OLD => src}/stylesheets/variant.sass (100%)
 create mode 100644 client/src/utils/array.js
 rename client/{client_OLD/javascripts => src}/utils/datetime.js (82%)
 delete mode 100644 client/src/utils/language.js
 rename client/{client_OLD/javascripts => src}/utils/printDiagram.js (98%)
 rename client/{client_OLD/javascripts => src}/utils/squareId.js (81%)
 rename client/{client_OLD/javascripts => src}/utils/storage.js (100%)
 rename client/{client_OLD/javascripts => src}/variants/Alice.js (100%)
 rename client/{client_OLD/javascripts => src}/variants/Antiking.js (95%)
 rename client/{client_OLD/javascripts/variants/Atomic.js => src/variants/Atomic_OLD.js} (100%)
 rename client/{client_OLD/javascripts => src}/variants/Baroque.js (98%)
 rename client/{client_OLD/javascripts => src}/variants/Berolina.js (100%)
 rename client/{client_OLD/javascripts => src}/variants/Checkered.js (98%)
 rename client/{client_OLD/javascripts => src}/variants/Chess960.js (100%)
 rename client/{client_OLD/javascripts => src}/variants/Crazyhouse.js (100%)
 rename client/{client_OLD/javascripts => src}/variants/Dark.js (99%)
 rename client/{client_OLD/javascripts => src}/variants/Extinction.js (100%)
 rename client/{client_OLD/javascripts => src}/variants/Grand.js (97%)
 rename client/{client_OLD/javascripts => src}/variants/Losers.js (94%)
 rename client/{client_OLD/javascripts => src}/variants/Magnetic.js (100%)
 rename client/{client_OLD/javascripts => src}/variants/Marseille.js (99%)
 rename client/{client_OLD/javascripts => src}/variants/Switching.js (100%)
 rename client/{client_OLD/javascripts => src}/variants/Upsidedown.js (90%)
 rename client/{client_OLD/javascripts => src}/variants/Wildebeest.js (95%)
 rename client/{client_OLD/javascripts => src}/variants/Zen.js (100%)
 delete mode 100644 client/src/views/About.vue
 create mode 100644 client/src/views/Game.vue
 rename client/{client_OLD/javascripts/components/room.js => src/views/Hall.vue} (95%)
 rename client/{client_OLD/javascripts/components/tabGames.js => src/views/MyGames.vue} (93%)
 rename client/{client_OLD/javascripts/components/problems.js => src/views/Problems.vue} (97%)
 delete mode 100644 client/src/views/Test.vue
 rename client/{client_OLD/javascripts/components/correspondance.js => src/views/correspondance_merge_hall.js} (100%)
 rename client/{client_OLD/javascripts/components => src/views}/rules.js (100%)
 rename client/src/{modals => }/welcome/en.pug (100%)
 rename client/src/{modals => }/welcome/es.pug (100%)
 rename client/src/{modals => }/welcome/fr.pug (100%)

diff --git a/client/client_OLD/javascripts/components/variantSummary.js b/client/client_OLD/javascripts/components/variantSummary.js
deleted file mode 100644
index 16b06ce4..00000000
--- a/client/client_OLD/javascripts/components/variantSummary.js
+++ /dev/null
@@ -1,30 +0,0 @@
-// Show a variant summary on index
-Vue.component('my-variant-summary', {
-	props: ['vobj','index'],
-	template: `
-		<div class="variant col-sm-12 col-md-5 col-lg-4" :id="vobj.name"
-			:class="{'col-md-offset-1': index%2==0, 'col-lg-offset-2': index%2==0}">
-			<a :href="url">
-				<h4 class="boxtitle text-center">
-					{{ vobj.name }}
-					<span class="count-players">
-						/ {{ vobj.count }}
-					</span>
-				</h4>
-				<p class="description text-center">
-					{{ translate(vobj.desc) }}
-				</p>
-			</a>
-		</div>
-	`,
-	computed: {
-		url: function() {
-			return "/" + this.vobj.name;
-		},
-	},
-	methods: {
-		translate: function(text) {
-			return translations[text];
-		},
-	},
-})
diff --git a/client/client_OLD/javascripts/utils/array.js b/client/client_OLD/javascripts/utils/array.js
deleted file mode 100644
index ab1ae10d..00000000
--- a/client/client_OLD/javascripts/utils/array.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// Remove item in array (if present)
-var removeItem = function(array, rfun)
-{
-	let index = array.findIndex(rfun);
-	if (index >= 0)
-		array.splice(index, 1);
-}
-
-// Remove several item matching a condition
-var removeMultiple = function(array, rfun)
-{
-	// Reverse loop because of the splice below
-	for (let i=array.length-1; i>=0; i--)
-	{
-		if (rfun(array[i]))
-			array.splice(i, 1);
-	}
-}
-
-// Double array intialization
-var doubleArray = function(size1, size2, initElem)
-{
-	return _.map(_.range(size1), () => {
-		return _.map(_.range(size2), () => {
-			return initElem; //can be undefined
-		})
-	});
-}
-
-var copyDoubleArray = function(arr)
-{
-	return _.map(_.range(arr.length), (el1,i) => {
-		return _.map(_.range(arr[0].length), (el2,j) => {
-			return arr[i][j];
-		})
-	});
-}
diff --git a/client/client_OLD/javascripts/variant.js b/client/client_OLD/javascripts/variant.js
deleted file mode 100644
index c45de0ac..00000000
--- a/client/client_OLD/javascripts/variant.js
+++ /dev/null
@@ -1,70 +0,0 @@
-new Vue({
-	el: "#VueElement",
-	data: {
-		display: "", //default to main hall; see "created()" function
-		gameRef: undefined, //...for now
-		probId: undefined,
-		conn: null,
-		mode: "analyze",
-		allowChat: false,
-		allowMovelist: true,
-		// Settings initialized with values from localStorage
-		settings:	{
-			bcolor: localStorage["bcolor"] || "lichess",
-			sound: parseInt(localStorage["sound"]) || 2,
-			hints: parseInt(localStorage["hints"]) || 1,
-			coords: !!eval(localStorage["coords"]),
-			highlight: !!eval(localStorage["highlight"]),
-			sqSize: parseInt(localStorage["sqSize"]),
-		},
-	},
-	created: function() {
-		window.onhashchange = this.setDisplay;
-		if (!!localStorage["variant"])
-			location.hash = "#game?id=" + localStorage["gameId"];
-		else
-			this.setDisplay();
-		// Our ID, always set (DB id if registered, collision-free random string otherwise)
-		this.myid = user.id || localStorage["myid"] || "anon-" + getRandString();
-		this.conn = new WebSocket(socketUrl + "/?sid=" + this.myid + "&page=" + variant.id);
-		const socketCloseListener = () => {
-			this.conn = new WebSocket(socketUrl + "/?sid=" + this.myid + "&page=" + variant.id);
-		}
-		this.conn.onclose = socketCloseListener;
-	},
-	methods: {
-		// Game is over, clear storage and put it in indexedDB
-		archiveGame: function() {
-			// TODO: ...
-			//clearStorage();
-		},
-		setDisplay: function() {
-			// Prevent set display if there is a running game
-			if (!!localStorage["variant"])
-				return;
-			if (!location.hash)
-				location.hash = "#room"; //default
-			const hashParts = location.hash.substr(1).split("?");
-			this.display = hashParts[0];
-			if (hashParts[0] == "problems" && !!hashParts[1])
-			{
-				// Show a specific problem
-				this.probId = hashParts[1].split("=")[1];
-			}
-			else if (hashParts[0] == "game" && !!hashParts[1])
-			{
-				// Show a specific game, maybe with a user ID
-				const params = hashParts[1].split("&").filter(h => h.split("=")[1]);
-				// TODO: Vue.set(...) probably required here
-				this.gameRef = {
-					id: params[0],
-					uid: params[1], //may be undefined
-				};
-			}
-			// Close menu on small screens:
-			let menuToggle = document.getElementById("drawer-control");
-			if (!!menuToggle)
-				menuToggle.checked = false;
-		},
-	},
-});
diff --git a/client/client_OLD/views/app.pug b/client/client_OLD/views/app.pug
deleted file mode 100644
index 7137b191..00000000
--- a/client/client_OLD/views/app.pug
+++ /dev/null
@@ -1,51 +0,0 @@
-doctype html
-html
-		main#VueElement
-			my-upsert-user
-			.container
-						v-bind:vobj="v" v-bind:index="idx" v-bind:key="v.name")
-				.row(v-show="display=='correspondance'")
-					my-correspondance
-				.row
-					my-room(v-show="display=='room'" :conn="conn" :settings="settings")
-					my-tab-games(v-show="display=='tabGames'")
-					my-rules(v-show="display=='rules'" :settings="settings")
-					my-problems(v-show="display=='problems'" :prob-id="probId" :settings="settings")
-					my-game(v-show="display=='game'" :game-ref="gameRef" :conn="conn"
-						:allow-chat="allowChat" :allow-movelist="allowMovelist"
-						:mode="mode" :settings="settings" @game-over="archiveGame")
-
-		// TODO: get rid of underscore
-		// (used essentially for _.random(), _.sample() and _.range())
-		script(src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js")
-		// TODO: add only the necessary icons to mini-css custom build
-		script(src="//unpkg.com/feather-icons")
-		script(src="/javascripts/utils/printDiagram.js")
-		script(src="/javascripts/utils/datetime.js")
-		script(src="/javascripts/utils/squareId.js")
-		script(src="/javascripts/utils/misc.js")
-		script(src="/javascripts/utils/ajax.js")
-		script(src="/javascripts/utils/array.js")
-		script(src="/javascripts/shared/nbPlayers.js")
-		script(src="/javascripts/shared/challengeCheck.js")
-		script(src="/javascripts/shared/userCheck.js")
-		script(src="/javascripts/components/upsertUser.js")
-		script(src="/javascripts/components/variantSummary.js")
-		script(src="/javascripts/components/correspondance.js")
-		script(src="/javascripts/components/board.js")
-		script(src="/javascripts/components/chat.js")
-		script(src="/javascripts/components/gameList.js")
-		script(src="/javascripts/components/challengeList.js")
-		script(src="/javascripts/components/moveList.js")
-		script(src="/javascripts/components/game.js")
-		script(src="/javascripts/components/rules.js")
-		script(src="/javascripts/components/room.js")
-		script(src="/javascripts/components/tabGames.js")
-		script(src="/javascripts/components/problemSummary.js")
-		script(src="/javascripts/components/problems.js")
-		script(src="/javascripts/base_rules.js")
-		script(src="/javascripts/contactForm.js")
-		script(src="/javascripts/socket_url.js")
-		script(src="/javascripts/index.js")
-		script(src="/javascripts/variant.js")
-		script(src="/javascripts/app.js")
diff --git a/client/src/App.vue b/client/src/App.vue
index 27e5582a..745dd799 100644
--- a/client/src/App.vue
+++ b/client/src/App.vue
@@ -12,7 +12,7 @@
         .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
 					img(src="./assets/images/index/unicorn.svg")
 					.info-container
-						p vchess.club {{ $lang }}
+						p vchess.club
 					img(src="./assets/images/index/wildebeest.svg")
     .row
       // Menu (top of page):
@@ -56,6 +56,9 @@
 					a(href="https://github.com/yagu0/vchess") Source code
 					p.clickable(onClick="doClick('modalContact')")
 						= translations["Contact form"]
+  //my-game(:game-ref="gameRef" :mode="mode" :settings="settings" @game-over="archiveGame")
+  //// TODO: add only the necessary icons to mini-css custom build
+  //script(src="//unpkg.com/feather-icons")
 </template>
 
 <script>
diff --git a/client/client_OLD/javascripts/base_rules.js b/client/src/base_rules.js
similarity index 98%
rename from client/client_OLD/javascripts/base_rules.js
rename to client/src/base_rules.js
index 9f6ec9ec..98c27af7 100644
--- a/client/client_OLD/javascripts/base_rules.js
+++ b/client/src/base_rules.js
@@ -236,28 +236,28 @@ class ChessRules
 		// Shuffle pieces on first and last rank
 		for (let c of ["w","b"])
 		{
-			let positions = _.range(8);
+			let positions = range(8);
 
 			// Get random squares for bishops
-			let randIndex = 2 * _.random(3);
+			let randIndex = 2 * random(4);
 			const bishop1Pos = positions[randIndex];
 			// The second bishop must be on a square of different color
-			let randIndex_tmp = 2 * _.random(3) + 1;
+			let randIndex_tmp = 2 * random(4) + 1;
 			const bishop2Pos = positions[randIndex_tmp];
 			// Remove chosen squares
 			positions.splice(Math.max(randIndex,randIndex_tmp), 1);
 			positions.splice(Math.min(randIndex,randIndex_tmp), 1);
 
 			// Get random squares for knights
-			randIndex = _.random(5);
+			randIndex = random(6);
 			const knight1Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
-			randIndex = _.random(4);
+			randIndex = random(5);
 			const knight2Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
 			// Get random square for queen
-			randIndex = _.random(3);
+			randIndex = random(4);
 			const queenPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
@@ -1132,7 +1132,7 @@ class ChessRules
 		let moves1 = this.getAllValidMoves("computer");
 
 		// Can I mate in 1 ? (for Magnetic & Extinction)
-		for (let i of _.shuffle(_.range(moves1.length)))
+		for (let i of shuffle(range(moves1.length)))
 		{
 			this.play(moves1[i]);
 			let finish = (Math.abs(this.evalPosition()) >= V.THRESHOLD_MATE);
@@ -1190,7 +1190,7 @@ class ChessRules
 		let candidates = [0]; //indices of candidates moves
 		for (let j=1; j<moves1.length && moves1[j].eval == moves1[0].eval; j++)
 			candidates.push(j);
-		let currentBest = moves1[_.sample(candidates, 1)];
+		let currentBest = moves1[sample(candidates)];
 
 		// From here, depth >= 3: may take a while, so we control time
 		const timeStart = Date.now();
@@ -1218,7 +1218,7 @@ class ChessRules
 		candidates = [0];
 		for (let j=1; j<moves1.length && moves1[j].eval == moves1[0].eval; j++)
 			candidates.push(j);
-		return moves1[_.sample(candidates, 1)];
+		return moves1[sample(candidates)];
 	}
 
 	alphabeta(depth, alpha, beta)
diff --git a/client/src/components/Settings.vue b/client/src/components/Settings.vue
index 34431a5d..7aa0162e 100644
--- a/client/src/components/Settings.vue
+++ b/client/src/components/Settings.vue
@@ -7,28 +7,28 @@ div
       h3#settingsTitle.section {{ $tr["Preferences"] }}
       fieldset
         label(for="setSqSize") {{ $tr["Square size (in pixels). 0 for 'adaptative'"] }}
-        input#setSqSize(type="number" v-model="settings.sqSize")
+        input#setSqSize(type="number" v-model="$settings.sqSize")
       fieldset
         label(for="selectHints") {{ $tr["Show move hints?"] }}
-        select#setHints(v-model="settings.hints")
+        select#setHints(v-model="$settings.hints")
           option(value="0") {{ $tr["None"] }}
           option(value="1") {{ $tr["Moves from a square"] }}
           option(value="2") {{ $tr["Pieces which can move"] }}
       fieldset
         label(for="setHighlight") {{ $tr["Highlight squares? (Last move & checks)"] }}
-        input#setHighlight(type="checkbox" v-model="settings.highlight")
+        input#setHighlight(type="checkbox" v-model="$settings.highlight")
       fieldset
         label(for="setCoords") {{ $tr["Show board coordinates?"] }}
-        input#setCoords(type="checkbox" v-model="settings.coords")
+        input#setCoords(type="checkbox" v-model="$settings.coords")
       fieldset
         label(for="selectColor") {{ $tr["Board colors"] }}
-        select#setBcolor(v-model="settings.bcolor")
+        select#setBcolor(v-model="$settings.bcolor")
           option(value="lichess") {{ $tr["brown"] }}
           option(value="chesscom") {{ $tr["green"] }}
           option(value="chesstempo") {{ $tr["blue"] }}
       fieldset
         label(for="selectSound") {{ $tr["Play sounds?"] }}
-        select#setSound(v-model="settings.sound")
+        select#setSound(v-model="$settings.sound")
           option(value="0") {{ $tr["None"] }}
           option(value="1") {{ $tr["New game"] }}
           option(value="2") {{ $tr["All"] }}
@@ -37,7 +37,7 @@ div
 <script>
 export default {
   name: "Settings",
-  props: ["settings"],
+  //props: ["settings"],
 	methods: {
     updateSettings: function(event) {
       const propName =
diff --git a/client/client_OLD/javascripts/components/board.js b/client/src/components/board.js
similarity index 98%
rename from client/client_OLD/javascripts/components/board.js
rename to client/src/components/board.js
index a59e41f7..cd3373c4 100644
--- a/client/client_OLD/javascripts/components/board.js
+++ b/client/src/components/board.js
@@ -78,7 +78,7 @@ Vue.component('my-board', {
 					'clearer': true,
 				},
 			},
-			[_.range(sizeX).map(i => {
+			[...Array(sizeX).keys()].map(i => {
 				let ci = (this.orientation=='w' ? i : sizeX-i-1);
 				return h(
 					'div',
@@ -88,7 +88,7 @@ Vue.component('my-board', {
 						},
 						style: { 'opacity': this.choices.length>0?"0.5":"1" },
 					},
-					_.range(sizeY).map(j => {
+					[...Array(sizeY).keys()].map(j => {
 						let cj = (this.orientation=='w' ? j : sizeY-j-1);
 						let elems = [];
 						if (this.vr.board[ci][cj] != V.EMPTY && (variant.name!="Dark"
@@ -140,7 +140,7 @@ Vue.component('my-board', {
 									'in-shadow': variant.name=="Dark" && !this.gameOver
 										&& this.mode != "analyze"
 										&& !this.vr.enlightened[this.userColor][ci][cj],
-									'highlight': showLight && !!lm && _.isMatch(lm.end, {x:ci,y:cj}),
+									'highlight': showLight && !!lm && lm.end.x == ci && lm.end.y == cj,
 									'incheck': showLight && incheckSq[ci][cj],
 								},
 								attrs: {
diff --git a/client/client_OLD/javascripts/components/challengeList.js b/client/src/components/challengeList.js
similarity index 100%
rename from client/client_OLD/javascripts/components/challengeList.js
rename to client/src/components/challengeList.js
diff --git a/client/client_OLD/javascripts/components/chat.js b/client/src/components/chat.js
similarity index 100%
rename from client/client_OLD/javascripts/components/chat.js
rename to client/src/components/chat.js
diff --git a/client/client_OLD/javascripts/components/game.js b/client/src/components/game.js
similarity index 100%
rename from client/client_OLD/javascripts/components/game.js
rename to client/src/components/game.js
diff --git a/client/client_OLD/javascripts/components/gameList.js b/client/src/components/gameList.js
similarity index 100%
rename from client/client_OLD/javascripts/components/gameList.js
rename to client/src/components/gameList.js
diff --git a/client/client_OLD/javascripts/components/moveList.js b/client/src/components/moveList.js
similarity index 100%
rename from client/client_OLD/javascripts/components/moveList.js
rename to client/src/components/moveList.js
diff --git a/client/client_OLD/javascripts/components/problemSummary.js b/client/src/components/problemSummary.js
similarity index 100%
rename from client/client_OLD/javascripts/components/problemSummary.js
rename to client/src/components/problemSummary.js
diff --git a/client/client_OLD/javascripts/components/upsertUser.js b/client/src/components/upsertUser.js
similarity index 100%
rename from client/client_OLD/javascripts/components/upsertUser.js
rename to client/src/components/upsertUser.js
diff --git a/client/client_OLD/javascripts/data/challengeCheck.js b/client/src/data/challengeCheck.js
similarity index 100%
rename from client/client_OLD/javascripts/data/challengeCheck.js
rename to client/src/data/challengeCheck.js
diff --git a/client/client_OLD/javascripts/data/nbPlayers.js b/client/src/data/nbPlayers.js
similarity index 100%
rename from client/client_OLD/javascripts/data/nbPlayers.js
rename to client/src/data/nbPlayers.js
diff --git a/client/client_OLD/javascripts/data/userCheck.js b/client/src/data/userCheck.js
similarity index 100%
rename from client/client_OLD/javascripts/data/userCheck.js
rename to client/src/data/userCheck.js
diff --git a/client/src/main.js b/client/src/main.js
index 4659a5b0..c48985dd 100644
--- a/client/src/main.js
+++ b/client/src/main.js
@@ -12,9 +12,6 @@ new Vue({
   render: function(h) {
     return h(App);
   },
-//  data: {
-//    lang: "",
-//  },
   watch: {
     $lang: async function(newLang) {
       // Fill modalWelcome, and import translations from "./translations/$lang.js"
@@ -42,11 +39,26 @@ new Vue({
 		// NOTE: in this version, we don't say on which page we are, yet
 		// ==> we'll say "enter/leave" page XY (in fact juste "enter", seemingly)
 		Vue.prototype.$conn = new WebSocket(params.socketUrl + "/?sid=" + myid);
+		// Settings initialized with values from localStorage
+		Vue.prototype.$settings = {
+			bcolor: localStorage["bcolor"] || "lichess",
+			sound: parseInt(localStorage["sound"]) || 2,
+			hints: parseInt(localStorage["hints"]) || 1,
+			coords: !!eval(localStorage["coords"]),
+			highlight: !!eval(localStorage["highlight"]),
+			sqSize: parseInt(localStorage["sqSize"]),
+		};
+		const socketCloseListener = () => {
+			Vue.prototype.$conn = new WebSocket(params.socketUrl + "/?sid=" + myid);
+		}
+		Vue.prototype.$conn.onclose = socketCloseListener;
 		//TODO: si une partie en cours dans storage, rediriger vers cette partie
 		//(à condition que l'URL n'y corresponde pas déjà !)
 		// TODO: à l'arrivée sur le site : set peerID (un identifiant unique
 		// en tout cas...) si pas trouvé dans localStorage "myid"
 		// (l'identifiant de l'utilisateur si connecté)
+//		if (!!localStorage["variant"])
+//			location.hash = "#game?id=" + localStorage["gameId"];
 	},
 	// Later, for icons (if using feather):
 //	mounted: function() {
@@ -66,3 +78,7 @@ new Vue({
 // problems: on-demand
 //
 // See https://router.vuejs.org/guide/essentials/dynamic-matching.html#reacting-to-params-changes
+//	created: function() {
+//		window.onhashchange = this.setDisplay;
+//	},
+//});
diff --git a/client/client_OLD/javascripts/playCompMove.js b/client/src/playCompMove.js
similarity index 100%
rename from client/client_OLD/javascripts/playCompMove.js
rename to client/src/playCompMove.js
diff --git a/client/src/router.js b/client/src/router.js
index 1507c8e7..08bbf88b 100644
--- a/client/src/router.js
+++ b/client/src/router.js
@@ -31,5 +31,6 @@ export default new Router({
 			name: "test",
 			component: loadView("Test"),
 		},
+    // TODO: gameRef, problemId: https://router.vuejs.org/guide/essentials/dynamic-matching.html
   ]
 });
diff --git a/client/client_OLD/stylesheets/_users.sass b/client/src/stylesheets/_users.sass
similarity index 100%
rename from client/client_OLD/stylesheets/_users.sass
rename to client/src/stylesheets/_users.sass
diff --git a/client/client_OLD/stylesheets/index.sass b/client/src/stylesheets/index.sass
similarity index 100%
rename from client/client_OLD/stylesheets/index.sass
rename to client/src/stylesheets/index.sass
diff --git a/client/client_OLD/stylesheets/layout.sass b/client/src/stylesheets/layout.sass
similarity index 100%
rename from client/client_OLD/stylesheets/layout.sass
rename to client/src/stylesheets/layout.sass
diff --git a/client/client_OLD/stylesheets/variant.sass b/client/src/stylesheets/variant.sass
similarity index 100%
rename from client/client_OLD/stylesheets/variant.sass
rename to client/src/stylesheets/variant.sass
diff --git a/client/src/utils/array.js b/client/src/utils/array.js
new file mode 100644
index 00000000..a8466c66
--- /dev/null
+++ b/client/src/utils/array.js
@@ -0,0 +1,24 @@
+// Remove item(s) in array (if present)
+export function remove(array, rfun, all)
+{
+  const index = array.findIndex(rfun);
+  if (index >= 0)
+  {
+    array.splice(index, 1);
+    if (!!all)
+    {
+      // Reverse loop because of the splice below
+      for (let i=array.length-1; i>=index; i--)
+      {
+        if (rfun(array[i]))
+          array.splice(i, 1);
+      }
+    }
+  }
+}
+
+// Double array intialization
+export function init(size1, size2, initElem)
+{
+  return [...Array(size1)].map(e => Array(size2).fill(initElem));
+}
diff --git a/client/client_OLD/javascripts/utils/datetime.js b/client/src/utils/datetime.js
similarity index 82%
rename from client/client_OLD/javascripts/utils/datetime.js
rename to client/src/utils/datetime.js
index 4f965185..3075bffc 100644
--- a/client/client_OLD/javascripts/utils/datetime.js
+++ b/client/src/utils/datetime.js
@@ -3,12 +3,12 @@ function zeroPad(x)
 	return (x<10 ? "0" : "") + x;
 }
 
-function getDate(d)
+export function getDate(d)
 {
 	return d.getFullYear() + '-' + zeroPad(d.getMonth()+1) + '-' + zeroPad(d.getDate());
 }
 
-function getTime(d)
+export function getTime(d)
 {
 	return zeroPad(d.getHours()) + ":" + zeroPad(d.getMinutes()) + ":" +
 		zeroPad(d.getSeconds());
diff --git a/client/src/utils/language.js b/client/src/utils/language.js
deleted file mode 100644
index cff89aed..00000000
--- a/client/src/utils/language.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// TODO: adapt for client side https://stackoverflow.com/a/4079798
-// ==> Each translation file should be loaded dynamically
-// (each rules definition too, + modal welcome)
-
-// Select a language based on browser preferences, or cookie
-module.exports = function(req, res)
-{
-	// If preferred language already set:
-	if (!!req.cookies["lang"])
-		return req.cookies["lang"];
-
-	// Else: search and set it
-	const supportedLang = ["en","es","fr"];
-	const langString = req.headers["accept-language"];
-	let langArray = langString
-		.replace(/;q=[0-9.]+/g, "") //priority
-		.replace(/-[A-Z]+/g, "") //region (skipped for now...)
-		.split(",") //may have some duplicates, but removal is too costly
-	let bestLang = "en"; //default: English
-	for (let lang of langArray)
-	{
-		if (supportedLang.includes(lang))
-		{
-			bestLang = lang;
-			break;
-		}
-	}
-	// Cookie expires in 183 days (expressed in milliseconds)
-	res.cookie('lang', bestLang, { maxAge: 183*24*3600*1000 });
-	return bestLang;
-}
diff --git a/client/src/utils/misc.js b/client/src/utils/misc.js
index 178f3cfe..4a2abf71 100644
--- a/client/src/utils/misc.js
+++ b/client/src/utils/misc.js
@@ -23,6 +23,42 @@ export const util =
     return defaut; //cookie not found
   },
 
+  random: function(min, max)
+  {
+    if (!max)
+    {
+      max = min;
+      min = 0;
+    }
+    return Math.floor(Math.random() * (max - min) ) + min;
+  },
+
+  // Inspired by https://github.com/jashkenas/underscore/blob/master/underscore.js
+  sample: function(arr, n)
+  {
+    n = n || 1;
+    let cpArr = arr.map(e => e);
+    for (let index = 0; index < n; index++)
+    {
+      const rand = getRandInt(index, n);
+      const temp = cpArr[index];
+      cpArr[index] = cpArr[rand];
+      cpArr[rand] = temp;
+    }
+    return cpArr.slice(0, n);
+  },
+
+  shuffle: function(arr)
+  {
+    return sample(arr, arr.length);
+  },
+
+  range: function(max)
+  {
+    return [...Array(max).keys()];
+  }
+
+  // TODO: rename into "cookie" et supprimer les deux ci-dessous
   // Random (enough) string for socket and game IDs
   getRandString: function()
   {
@@ -35,9 +71,4 @@ export const util =
   {
     document.getElementById(elemId).click(); //or ".checked = true"
   },
-
-  translate: function(msg)
-  {
-    return translations[msg];
-  },
 };
diff --git a/client/client_OLD/javascripts/utils/printDiagram.js b/client/src/utils/printDiagram.js
similarity index 98%
rename from client/client_OLD/javascripts/utils/printDiagram.js
rename to client/src/utils/printDiagram.js
index ba7b1e97..f3f77d8a 100644
--- a/client/client_OLD/javascripts/utils/printDiagram.js
+++ b/client/src/utils/printDiagram.js
@@ -73,7 +73,7 @@ function getShadowArray(shadow)
 
 // args: object with position (mandatory), and
 // orientation, marks, shadow (optional)
-function getDiagram(args)
+export function getDiagram(args)
 {
 	// Obtain the array of pieces images names:
 	const board = V.GetBoard(args.position);
diff --git a/client/client_OLD/javascripts/utils/squareId.js b/client/src/utils/squareId.js
similarity index 81%
rename from client/client_OLD/javascripts/utils/squareId.js
rename to client/src/utils/squareId.js
index 8fec48eb..a68b51f9 100644
--- a/client/client_OLD/javascripts/utils/squareId.js
+++ b/client/src/utils/squareId.js
@@ -1,12 +1,12 @@
 // Get the identifier of a HTML square from its numeric coordinates o.x,o.y.
-function getSquareId(o)
+export function getSquareId(o)
 {
 	// NOTE: a separator is required to allow any size of board
 	return  "sq-" + o.x + "-" + o.y;
 }
 
 // Inverse function
-function getSquareFromId(id)
+export function getSquareFromId(id)
 {
 	const idParts = id.split('-');
 	return [parseInt(idParts[1]), parseInt(idParts[2])];
diff --git a/client/client_OLD/javascripts/utils/storage.js b/client/src/utils/storage.js
similarity index 100%
rename from client/client_OLD/javascripts/utils/storage.js
rename to client/src/utils/storage.js
diff --git a/client/client_OLD/javascripts/variants/Alice.js b/client/src/variants/Alice.js
similarity index 100%
rename from client/client_OLD/javascripts/variants/Alice.js
rename to client/src/variants/Alice.js
diff --git a/client/client_OLD/javascripts/variants/Antiking.js b/client/src/variants/Antiking.js
similarity index 95%
rename from client/client_OLD/javascripts/variants/Antiking.js
rename to client/src/variants/Antiking.js
index 66eb9f0a..890da580 100644
--- a/client/client_OLD/javascripts/variants/Antiking.js
+++ b/client/src/variants/Antiking.js
@@ -150,25 +150,25 @@ class AntikingRules extends ChessRules
 		let antikingPos = { "w": -1, "b": -1 };
 		for (let c of ["w","b"])
 		{
-			let positions = _.range(8);
+			let positions = range(8);
 
 			// Get random squares for bishops, but avoid corners; because,
 			// if an antiking blocks a cornered bishop, it can never be checkmated
-			let randIndex = 2 * _.random(1,3);
+			let randIndex = 2 * random(1,4);
 			const bishop1Pos = positions[randIndex];
-			let randIndex_tmp = 2 * _.random(2) + 1;
+			let randIndex_tmp = 2 * random(3) + 1;
 			const bishop2Pos = positions[randIndex_tmp];
 			positions.splice(Math.max(randIndex,randIndex_tmp), 1);
 			positions.splice(Math.min(randIndex,randIndex_tmp), 1);
 
-			randIndex = _.random(5);
+			randIndex = random(6);
 			const knight1Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
-			randIndex = _.random(4);
+			randIndex = random(5);
 			const knight2Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
-			randIndex = _.random(3);
+			randIndex = random(4);
 			const queenPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
@@ -177,7 +177,7 @@ class AntikingRules extends ChessRules
 			const rook2Pos = positions[2];
 
 			// Random squares for antikings
-			antikingPos[c] = _.random(7);
+			antikingPos[c] = random(8);
 
 			pieces[c][rook1Pos] = 'r';
 			pieces[c][knight1Pos] = 'n';
diff --git a/client/client_OLD/javascripts/variants/Atomic.js b/client/src/variants/Atomic_OLD.js
similarity index 100%
rename from client/client_OLD/javascripts/variants/Atomic.js
rename to client/src/variants/Atomic_OLD.js
diff --git a/client/client_OLD/javascripts/variants/Baroque.js b/client/src/variants/Baroque.js
similarity index 98%
rename from client/client_OLD/javascripts/variants/Baroque.js
rename to client/src/variants/Baroque.js
index d06b99c3..c394257a 100644
--- a/client/client_OLD/javascripts/variants/Baroque.js
+++ b/client/src/variants/Baroque.js
@@ -548,34 +548,34 @@ class BaroqueRules extends ChessRules
 		// Shuffle pieces on first and last rank
 		for (let c of ["w","b"])
 		{
-			let positions = _.range(8);
+			let positions = range(8);
 			// Get random squares for every piece, totally freely
 
-			let randIndex = _.random(7);
+			let randIndex = random(8);
 			const bishop1Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
-			randIndex = _.random(6);
+			randIndex = random(7);
 			const bishop2Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
-			randIndex = _.random(5);
+			randIndex = random(6);
 			const knight1Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
-			randIndex = _.random(4);
+			randIndex = random(5);
 			const knight2Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
-			randIndex = _.random(3);
+			randIndex = random(4);
 			const queenPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
-			randIndex = _.random(2);
+			randIndex = random(3);
 			const kingPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
-			randIndex = _.random(1);
+			randIndex = random(2);
 			const rookPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 			const immobilizerPos = positions[0];
diff --git a/client/client_OLD/javascripts/variants/Berolina.js b/client/src/variants/Berolina.js
similarity index 100%
rename from client/client_OLD/javascripts/variants/Berolina.js
rename to client/src/variants/Berolina.js
diff --git a/client/client_OLD/javascripts/variants/Checkered.js b/client/src/variants/Checkered.js
similarity index 98%
rename from client/client_OLD/javascripts/variants/Checkered.js
rename to client/src/variants/Checkered.js
index 9de46ff1..1f6750b9 100644
--- a/client/client_OLD/javascripts/variants/Checkered.js
+++ b/client/src/variants/Checkered.js
@@ -55,8 +55,8 @@ class CheckeredRules extends ChessRules
 		super.setFlags(fenflags); //castleFlags
 		this.pawnFlags =
 		{
-			"w": _.map(_.range(8), i => true), //pawns can move 2 squares?
-			"b": _.map(_.range(8), i => true)
+			"w": [...Array(8).fill(true)], //pawns can move 2 squares?
+			"b": [...Array(8).fill(true)],
 		};
 		if (!fenflags)
 			return;
diff --git a/client/client_OLD/javascripts/variants/Chess960.js b/client/src/variants/Chess960.js
similarity index 100%
rename from client/client_OLD/javascripts/variants/Chess960.js
rename to client/src/variants/Chess960.js
diff --git a/client/client_OLD/javascripts/variants/Crazyhouse.js b/client/src/variants/Crazyhouse.js
similarity index 100%
rename from client/client_OLD/javascripts/variants/Crazyhouse.js
rename to client/src/variants/Crazyhouse.js
diff --git a/client/client_OLD/javascripts/variants/Dark.js b/client/src/variants/Dark.js
similarity index 99%
rename from client/client_OLD/javascripts/variants/Dark.js
rename to client/src/variants/Dark.js
index 3879d9df..76e9462e 100644
--- a/client/client_OLD/javascripts/variants/Dark.js
+++ b/client/src/variants/Dark.js
@@ -282,6 +282,6 @@ class DarkRules extends ChessRules
 		let candidates = [0];
 		for (let j=1; j<moves.length && moves[j].eval == moves[0].eval; j++)
 			candidates.push(j);
-		return moves[_.sample(candidates, 1)];
+		return moves[sample(candidates)];
 	}
 }
diff --git a/client/client_OLD/javascripts/variants/Extinction.js b/client/src/variants/Extinction.js
similarity index 100%
rename from client/client_OLD/javascripts/variants/Extinction.js
rename to client/src/variants/Extinction.js
diff --git a/client/client_OLD/javascripts/variants/Grand.js b/client/src/variants/Grand.js
similarity index 97%
rename from client/client_OLD/javascripts/variants/Grand.js
rename to client/src/variants/Grand.js
index 64088471..c056244d 100644
--- a/client/client_OLD/javascripts/variants/Grand.js
+++ b/client/src/variants/Grand.js
@@ -51,7 +51,7 @@ class GrandRules extends ChessRules
 
 	getCapturedFen()
 	{
-		let counts = _.map(_.range(14), 0);
+		let counts = [...Array(14).fill(0)];
 		let i = 0;
 		for (let j=0; j<V.PIECES.length; j++)
 		{
@@ -329,38 +329,38 @@ class GrandRules extends ChessRules
 		// Shuffle pieces on first and last rank
 		for (let c of ["w","b"])
 		{
-			let positions = _.range(10);
+			let positions = range(10);
 
 			// Get random squares for bishops
-			let randIndex = 2 * _.random(4);
+			let randIndex = 2 * random(5);
 			let bishop1Pos = positions[randIndex];
 			// The second bishop must be on a square of different color
-			let randIndex_tmp = 2 * _.random(4) + 1;
+			let randIndex_tmp = 2 * random(5) + 1;
 			let bishop2Pos = positions[randIndex_tmp];
 			// Remove chosen squares
 			positions.splice(Math.max(randIndex,randIndex_tmp), 1);
 			positions.splice(Math.min(randIndex,randIndex_tmp), 1);
 
 			// Get random squares for knights
-			randIndex = _.random(7);
+			randIndex = random(8);
 			let knight1Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
-			randIndex = _.random(6);
+			randIndex = random(7);
 			let knight2Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
 			// Get random square for queen
-			randIndex = _.random(5);
+			randIndex = random(6);
 			let queenPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
 			// ...random square for marshall
-			randIndex = _.random(4);
+			randIndex = random(5);
 			let marshallPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
 			// ...random square for cardinal
-			randIndex = _.random(3);
+			randIndex = random(4);
 			let cardinalPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
diff --git a/client/client_OLD/javascripts/variants/Losers.js b/client/src/variants/Losers.js
similarity index 94%
rename from client/client_OLD/javascripts/variants/Losers.js
rename to client/src/variants/Losers.js
index 33c81093..e1fb54c0 100644
--- a/client/client_OLD/javascripts/variants/Losers.js
+++ b/client/src/variants/Losers.js
@@ -133,33 +133,33 @@ class LosersRules extends ChessRules
 		// Shuffle pieces on first and last rank
 		for (let c of ["w","b"])
 		{
-			let positions = _.range(8);
+			let positions = range(8);
 
 			// Get random squares for bishops
-			let randIndex = 2 * _.random(3);
+			let randIndex = 2 * random(4);
 			let bishop1Pos = positions[randIndex];
 			// The second bishop must be on a square of different color
-			let randIndex_tmp = 2 * _.random(3) + 1;
+			let randIndex_tmp = 2 * random(4) + 1;
 			let bishop2Pos = positions[randIndex_tmp];
 			// Remove chosen squares
 			positions.splice(Math.max(randIndex,randIndex_tmp), 1);
 			positions.splice(Math.min(randIndex,randIndex_tmp), 1);
 
 			// Get random squares for knights
-			randIndex = _.random(5);
+			randIndex = random(6);
 			let knight1Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
-			randIndex = _.random(4);
+			randIndex = random(5);
 			let knight2Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
 			// Get random square for queen
-			randIndex = _.random(3);
+			randIndex = random(4);
 			let queenPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
 			// Random square for king (no castle)
-			randIndex = _.random(2);
+			randIndex = random(3);
 			let kingPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
diff --git a/client/client_OLD/javascripts/variants/Magnetic.js b/client/src/variants/Magnetic.js
similarity index 100%
rename from client/client_OLD/javascripts/variants/Magnetic.js
rename to client/src/variants/Magnetic.js
diff --git a/client/client_OLD/javascripts/variants/Marseille.js b/client/src/variants/Marseille.js
similarity index 99%
rename from client/client_OLD/javascripts/variants/Marseille.js
rename to client/src/variants/Marseille.js
index 7d83bd55..2d4ecfa4 100644
--- a/client/client_OLD/javascripts/variants/Marseille.js
+++ b/client/src/variants/Marseille.js
@@ -287,7 +287,7 @@ class MarseilleRules extends ChessRules
 			candidates.push(i);
 		}
 
-		const selected = doubleMoves[_.sample(candidates, 1)].moves;
+		const selected = doubleMoves[sample(candidates)].moves;
 		if (selected.length == 1)
 			return selected[0];
 		return selected;
diff --git a/client/client_OLD/javascripts/variants/Switching.js b/client/src/variants/Switching.js
similarity index 100%
rename from client/client_OLD/javascripts/variants/Switching.js
rename to client/src/variants/Switching.js
diff --git a/client/client_OLD/javascripts/variants/Upsidedown.js b/client/src/variants/Upsidedown.js
similarity index 90%
rename from client/client_OLD/javascripts/variants/Upsidedown.js
rename to client/src/variants/Upsidedown.js
index ecd1ff51..7198c0b1 100644
--- a/client/client_OLD/javascripts/variants/Upsidedown.js
+++ b/client/src/variants/Upsidedown.js
@@ -16,9 +16,9 @@ class UpsidedownRules extends ChessRules
 		let pieces = { "w": new Array(8), "b": new Array(8) };
 		for (let c of ["w","b"])
 		{
-			let positions = _.range(8);
+			let positions = range(8);
 
-			let randIndex = _.random(7);
+			let randIndex = random(8);
 			const kingPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
@@ -35,18 +35,18 @@ class UpsidedownRules extends ChessRules
 			positions.splice(knight1Index, 1);
 
 			// King+knight1 are on two consecutive squares: one light, one dark
-			randIndex = 2 * _.random(2);
+			randIndex = 2 * random(3);
 			const bishop1Pos = positions[randIndex];
-			let randIndex_tmp = 2 * _.random(2) + 1;
+			let randIndex_tmp = 2 * random(3) + 1;
 			const bishop2Pos = positions[randIndex_tmp];
 			positions.splice(Math.max(randIndex,randIndex_tmp), 1);
 			positions.splice(Math.min(randIndex,randIndex_tmp), 1);
 
-			randIndex = _.random(3);
+			randIndex = random(4);
 			const knight2Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
-			randIndex = _.random(2);
+			randIndex = random(3);
 			const queenPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
diff --git a/client/client_OLD/javascripts/variants/Wildebeest.js b/client/src/variants/Wildebeest.js
similarity index 95%
rename from client/client_OLD/javascripts/variants/Wildebeest.js
rename to client/src/variants/Wildebeest.js
index cb95318b..509586a9 100644
--- a/client/client_OLD/javascripts/variants/Wildebeest.js
+++ b/client/src/variants/Wildebeest.js
@@ -231,14 +231,14 @@ class WildebeestRules extends ChessRules
 		let pieces = { "w": new Array(10), "b": new Array(10) };
 		for (let c of ["w","b"])
 		{
-			let positions = _.range(11);
+			let positions = range(11);
 
 			// Get random squares for bishops + camels (different colors)
-			let randIndexes = _.sample(_.range(6), 2).map(i => { return 2*i; });
+			let randIndexes = sample(range(6), 2).map(i => { return 2*i; });
 			let bishop1Pos = positions[randIndexes[0]];
 			let camel1Pos = positions[randIndexes[1]];
 			// The second bishop (camel) must be on a square of different color
-			let randIndexes_tmp = _.sample(_.range(5), 2).map(i => { return 2*i+1; });
+			let randIndexes_tmp = sample(range(5), 2).map(i => { return 2*i+1; });
 			let bishop2Pos = positions[randIndexes_tmp[0]];
 			let camel2Pos = positions[randIndexes_tmp[1]];
 			for (let idx of randIndexes.concat(randIndexes_tmp)
@@ -247,19 +247,19 @@ class WildebeestRules extends ChessRules
 				positions.splice(idx, 1);
 			}
 
-			let randIndex = _.random(6);
+			let randIndex = random(7);
 			let knight1Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
-			randIndex = _.random(5);
+			randIndex = random(6);
 			let knight2Pos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
-			randIndex = _.random(4);
+			randIndex = random(5);
 			let queenPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
 			// Random square for wildebeest
-			randIndex = _.random(3);
+			randIndex = random(4);
 			let wildebeestPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
diff --git a/client/client_OLD/javascripts/variants/Zen.js b/client/src/variants/Zen.js
similarity index 100%
rename from client/client_OLD/javascripts/variants/Zen.js
rename to client/src/variants/Zen.js
diff --git a/client/src/views/About.vue b/client/src/views/About.vue
deleted file mode 100644
index 3fa28070..00000000
--- a/client/src/views/About.vue
+++ /dev/null
@@ -1,5 +0,0 @@
-<template>
-  <div class="about">
-    <h1>This is an about page</h1>
-  </div>
-</template>
diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue
new file mode 100644
index 00000000..2c877b7d
--- /dev/null
+++ b/client/src/views/Game.vue
@@ -0,0 +1,12 @@
+<template>
+  <div class="about">
+    <h1>This is an about page</h1>
+  </div>
+</template>
+	methods: {
+		// Game is over, clear storage and put it in indexedDB
+		archiveGame: function() {
+			// TODO: ...
+			//clearStorage();
+		},
+	},
diff --git a/client/client_OLD/javascripts/components/room.js b/client/src/views/Hall.vue
similarity index 95%
rename from client/client_OLD/javascripts/components/room.js
rename to client/src/views/Hall.vue
index fbf02ee1..c1ef7eb0 100644
--- a/client/client_OLD/javascripts/components/room.js
+++ b/client/src/views/Hall.vue
@@ -1,3 +1,21 @@
+<template>
+  <div class="home">
+    <Home msg="Welcome to Your Vue.js Apppp"/>
+  </div>
+</template>
+
+<script>
+// @ is an alias to /src
+import HelloWorld from "@/components/HelloWorld.vue";
+
+export default {
+  name: "home",
+  components: {
+    HelloWorld,
+  }
+};
+</script>
+
 // main playing hall: chat + online players + current challenges + button "new game"
 // TODO: my-challenge-list, gérant clicks sur challenges, affichage, réception/émission des infos sur challenges ; de même, my-player-list
 // TODO: si on est en train de jouer une partie, le notifier aux nouveaux connectés
diff --git a/client/src/views/Home.vue b/client/src/views/Home.vue
index 5b3c65e9..1eaa6b17 100644
--- a/client/src/views/Home.vue
+++ b/client/src/views/Home.vue
@@ -15,3 +15,34 @@ export default {
   }
 };
 </script>
+
+// Show a variant summary on index
+Vue.component('my-variant-summary', {
+	props: ['vobj','index'],
+	template: `
+		<div class="variant col-sm-12 col-md-5 col-lg-4" :id="vobj.name"
+			:class="{'col-md-offset-1': index%2==0, 'col-lg-offset-2': index%2==0}">
+			<a :href="url">
+				<h4 class="boxtitle text-center">
+					{{ vobj.name }}
+					<span class="count-players">
+						/ {{ vobj.count }}
+					</span>
+				</h4>
+				<p class="description text-center">
+					{{ translate(vobj.desc) }}
+				</p>
+			</a>
+		</div>
+	`,
+	computed: {
+		url: function() {
+			return "/" + this.vobj.name;
+		},
+	},
+	methods: {
+		translate: function(text) {
+			return translations[text];
+		},
+	},
+})
diff --git a/client/client_OLD/javascripts/components/tabGames.js b/client/src/views/MyGames.vue
similarity index 93%
rename from client/client_OLD/javascripts/components/tabGames.js
rename to client/src/views/MyGames.vue
index 3861dbac..5f183da1 100644
--- a/client/client_OLD/javascripts/components/tabGames.js
+++ b/client/src/views/MyGames.vue
@@ -1,3 +1,8 @@
+<template>
+  <div class="about">
+    <h1>This is an about page</h1>
+  </div>
+</template>
 // "My" games: tabs my archived local games, my correspondance games
 // + my imported games (of any type).
 // TODO: later, also add possibility to upload a game (parse PGN).
diff --git a/client/client_OLD/javascripts/components/problems.js b/client/src/views/Problems.vue
similarity index 97%
rename from client/client_OLD/javascripts/components/problems.js
rename to client/src/views/Problems.vue
index a9552923..b7f217d0 100644
--- a/client/client_OLD/javascripts/components/problems.js
+++ b/client/src/views/Problems.vue
@@ -1,3 +1,25 @@
+<template>
+  <div class="test">
+    <TestComp :vname="variant.name"/>
+  </div>
+</template>
+
+<script>
+// @ is an alias to /src
+import TestComp from "@/components/TestComp.vue";
+
+export default {
+  name: "test",
+  components: {
+    TestComp,
+  },
+	data: function() {
+		return {
+			variant: {name: "Atomic", id: 3},
+		};
+	}
+};
+</script>
 Vue.component('my-problems', {
 	props: ["probId","settings"],
 	data: function () {
diff --git a/client/src/views/Test.vue b/client/src/views/Test.vue
deleted file mode 100644
index 707f0b70..00000000
--- a/client/src/views/Test.vue
+++ /dev/null
@@ -1,22 +0,0 @@
-<template>
-  <div class="test">
-    <TestComp :vname="variant.name"/>
-  </div>
-</template>
-
-<script>
-// @ is an alias to /src
-import TestComp from "@/components/TestComp.vue";
-
-export default {
-  name: "test",
-  components: {
-    TestComp,
-  },
-	data: function() {
-		return {
-			variant: {name: "Atomic", id: 3},
-		};
-	}
-};
-</script>
diff --git a/client/client_OLD/javascripts/components/correspondance.js b/client/src/views/correspondance_merge_hall.js
similarity index 100%
rename from client/client_OLD/javascripts/components/correspondance.js
rename to client/src/views/correspondance_merge_hall.js
diff --git a/client/client_OLD/javascripts/components/rules.js b/client/src/views/rules.js
similarity index 100%
rename from client/client_OLD/javascripts/components/rules.js
rename to client/src/views/rules.js
diff --git a/client/src/modals/welcome/en.pug b/client/src/welcome/en.pug
similarity index 100%
rename from client/src/modals/welcome/en.pug
rename to client/src/welcome/en.pug
diff --git a/client/src/modals/welcome/es.pug b/client/src/welcome/es.pug
similarity index 100%
rename from client/src/modals/welcome/es.pug
rename to client/src/welcome/es.pug
diff --git a/client/src/modals/welcome/fr.pug b/client/src/welcome/fr.pug
similarity index 100%
rename from client/src/modals/welcome/fr.pug
rename to client/src/welcome/fr.pug
-- 
2.44.0