$ git clone https://ion.nu/git/pool
commit 833012da383a055e52083bdc453e91ede83d633c
Author: Alicia <...>
Date:   Wed Jun 27 20:13:31 2018 +0200

    Randomize ball arrangement at the start of the game.

diff --git a/match.c b/match.c
index f4f6940..7512b13 100644
--- a/match.c
+++ b/match.c
@@ -17,6 +17,7 @@
 */
 #include <stdlib.h>
 #include <stdio.h>
+#include <stdint.h>
 #include <unistd.h>
 #include <glib.h>
 #include <libwebsocket/websock.h>
@@ -26,6 +27,7 @@ struct game
 {
   websock_conn* ws[2];
   char* key;
+  uint8_t order[15];
 };
 struct game** waitinglist=0;
 unsigned int waitinglistlen=0;
@@ -78,6 +80,12 @@ static struct game* findgame(const char* key, websock_conn* ws)
     websock_write(ret->ws[1-start], "{\"cmd\":\"match\",\"yourturn\":false}", 32, WEBSOCK_TEXT);
   }
   g_mutex_unlock(&waitinglock);
+  if(ret) // Notify of the randomized initial ball arrangement
+  {
+    char cmd[64];
+    sprintf(cmd, "{\"cmd\":\"order\",\"order\":[%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu]}", ret->order[0], ret->order[1], ret->order[2], ret->order[3], ret->order[4], ret->order[5], ret->order[6], ret->order[7], ret->order[8], ret->order[9], ret->order[10], ret->order[11], ret->order[12], ret->order[13], ret->order[14], ret->order[15]);
+    websock_write(ws, cmd, strlen(cmd), WEBSOCK_TEXT);
+  }
   return ret;
 }
 
@@ -87,20 +95,36 @@ static struct game* newgame(char genkey, websock_conn* ws)
   struct game* game=malloc(sizeof(struct game));
   game->ws[0]=ws;
   game->ws[1]=0;
+  unsigned int i;
   if(genkey)
   {
     // Generate a random key, TODO: make sure it's unique
     game->key=malloc(5);
-    unsigned int i;
     for(i=0; i<4; ++i)
     {
       game->key[i]=KEYCHARS[random()%strlen(KEYCHARS)];
     }
     game->key[i]=0;
-printf("Generated key: '%s'\n", game->key);
+// printf("Generated key: '%s'\n", game->key);
   }else{
     game->key=0;
   }
+  // Generate an initial order for the balls, random but shared between the players
+  char order[]={1,2,3,4,8,5,6,7,9,10,11,12,13,14,15};
+  for(i=0; i<15; ++i)
+  {
+    if(i==4){continue;}
+    unsigned int swap=random()%14;
+    if(swap>=4){++swap;}
+    uint8_t tmp=order[i];
+    order[i]=order[swap];
+    order[swap]=tmp;
+  }
+  memcpy(game->order, order, sizeof(order));
+  char cmd[64];
+  sprintf(cmd, "{\"cmd\":\"order\",\"order\":[%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu,%hhu]}", order[0], order[1], order[2], order[3], order[4], order[5], order[6], order[7], order[8], order[9], order[10], order[11], order[12], order[13], order[14], order[15]);
+  websock_write(ws, cmd, strlen(cmd), WEBSOCK_TEXT);
+  // Add game to the list
   g_mutex_lock(&waitinglock);
   waitinglist=realloc(waitinglist, sizeof(struct game*)*++waitinglistlen);
   waitinglist[waitinglistlen-1]=game;
diff --git a/web/net.js b/web/net.js
index 107afc1..18c36f7 100644
--- a/web/net.js
+++ b/web/net.js
@@ -76,5 +76,6 @@ function netmsg(data)
       break;
     case 'match': myturn=data.yourturn; message=''; break;
     case 'pickcolor': mycolor=(data.color=='Whole'?'Half':'Whole'); break;
+    case 'order': placeballs(data.order); break;
   }
 }
diff --git a/web/pool.js b/web/pool.js
index 1bb4f38..ff9e26b 100644
--- a/web/pool.js
+++ b/web/pool.js
@@ -39,6 +39,33 @@ function endofshot()
   net_sync(!myturn);
 }
 
+function placeballs(order)
+{
+  if(!order)
+  {
+    // Randomize ball positions within the triangle, for singleplayer
+    order=[1,2,3,4,8,5,6,7,9,10,11,12,13,14,15];
+    for(var i=0; i<15; ++i)
+    {
+      // 8ball needs to be in center
+      if(i==4){continue;}
+      var swap=Math.floor(Math.random()*14);
+      if(swap>=4){++swap;}
+      var tmp=order[i];
+      order[i]=order[swap];
+      order[swap]=tmp;
+    }
+  }
+  var i=0;
+  for(var y=1; y<=5; ++y)
+  {
+    for(var x=0; x<y; ++x)
+    {
+      entities.push(new entity((654-38*(y-x*2))/2, 435-y*35, 38, 38, 'ball', order[i++]+'.png', i));
+    }
+  }
+}
+
 var message='Waiting for player 2';
 function init()
 {
@@ -79,15 +106,7 @@ function init()
   // Set up balls
   cueball=new entity(table.w/2-19, table.h*3/4-19, 38, 38, 'ball', '0.png', 'cue');
   entities.push(cueball);
-// TODO: Randomize ball positions within the triangle, but it has to be synced with the other player. Maybe the server should do it. 8ball needs to be in center
-  var i=1;
-  for(var y=1; y<=5; ++y)
-  {
-    for(var x=0; x<y; ++x)
-    {
-      entities.push(new entity((654-38*(y-x*2))/2, 435-y*35, 38, 38, 'ball', (i++)+'.png', i));
-    }
-  }
+  if(!sock){placeballs(false);} // For multiplayer the server places the balls
   // Borders
   entities.push(new entity(110, 0, 435, 68, 'border', false));
   entities.push(new entity(110, table.h-68, 435, 68, 'border', false));