<div id="gamestate">
</div>
<div id="gethearts">
  <span>Receive Color Message Here:</span>
  <button id="red">Red</button>
  <button id="blue">Blue</button>
  <button id="yellow">Yellow</button>
  <button id="resetr">Reset Code</button>
  <button id="backspace">Backspace</button>
  <div id="msg">...</div>
</div>
<div id="threads">
  <span>Encode Image to Colors Here:</span><div id="nextimg"><div class="img-wrapper"></div></div>
  <span>Concept Number:</span> <input type="number" placeholder="concept number" max=26 id="threadid"/>
  <span>Sequence Number:</span> <input type="number" placeholder="image number" max=26 id="imagenum"/>
  <button id="commit">Encode The Image</button>
  <button id="reset">Reset Game</button>
</div>
<div id="board"></div>
.hidden {
  display: none;
}

body {
  background-color: #aaa;
}

.chunk {
  border: 1px solid black;
}

#threads,#gethearts {
  height: 7em;
}

#nextimg {
  display: inline-block;
}

.img-wrapper {
  border: 1px solid black;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 4em;
  height: 4em;
  margin: .25em;
}

.main-img {
   max-width: 100%;
   max-height: 100%;
}

.row-img-wrapper {
  display: inline-grid;
  grid-template-columns: 5em 15em;
}

.row-img-wrapper .msg-wrapper {
  display: inline-grid;
  grid-template-columns: 100%;
}
let gameState = {};
let data = [];
let selectedImage = -1;

let fromDigitToHeart = function(anInt){
     switch (anInt){
      case 0:
        return "&#x2764;";
      case 1:
        return "&#x1F499;";//"&#x1F49A;";
      case 2:
        return "&#x1F49B;";
    } 
};

let fromIntToHeart = function(aLargeInt, padding){
  let base3 = aLargeInt.toString(3).padStart(padding,"0");
  let i = 0;
  let result = "";
  for (i = 0; i < base3.length; i++){
    result += fromDigitToHeart(parseInt(base3.substr(i,1)));
  }
  return {hearts:result,digits:base3};
}

let pushGame = function(gameState){
  displayGame(gameState);
}

let imageClickHandler = function(clickEvent){
  let clickedImageNumber = parseInt($(clickEvent.currentTarget).attr("data-val"));
  $("#nextimg").html(`<div class="img-wrapper"><img class="main-img" src="${makeURL(clickedImageNumber)}"/></div>`);
  selectedImage = clickedImageNumber;
}

let commitClickHandler = function(){
  let conceptNum = $("#threadid").val();
  let sequenceNum = $("#imagenum").val();
  let imageNum = selectedImage;
  if (!gameState.hasOwnProperty(conceptNum)){
    gameState[conceptNum] = {};
  }
  gameState[conceptNum][sequenceNum] = imageNum;
  pushGame(gameState);
}

let addImage = function(data){
  let conceptNum = parseInt(data[0])*9 + parseInt(data[1])*3 + parseInt(data[2]);
  let sequenceNum = parseInt(data[3])*9 + parseInt(data[4])*3 + parseInt(data[5]);
  let imageNum = parseInt(data[6])*81 + parseInt(data[7])*27 + parseInt(data[8])*9 + parseInt(data[9])*3 + parseInt(data[10]);
  if (!gameState.hasOwnProperty(conceptNum)){
    gameState[conceptNum] = {};
  }
  gameState[conceptNum][sequenceNum] = imageNum;
  pushGame(gameState);
}


let makeURL = function(imgID){
  return `https://prof.ninja/gameimages/${imgID}.png`
}

let initGame = function(){
  let i = 1;
  let imgs = [];
  for (i=1; i <= 118; i++){
    imgs.push(makeURL(i));
  }
  i =1;
  imgs.map(imgurl=>{
    $("#board").append(`
<div class="img-wrapper" data-val=${i}>
  <img class="main-img" src=${imgurl}>
</div>
`);
    i += 1;
  });
  
  $(".img-wrapper").on("click", imageClickHandler);
  $("#commit").on("click", commitClickHandler);
  $("#reset").on("click", function(){
    displayGame({});
  })
}

let displayGame = function(gameObj){
  //gameObj has keys as ints, and those have picture numbers:
  let keys = Object.keys(gameObj);
  keys.sort(function(a, b) {
    return a - b;
  });
  $("#gamestate").html("");
  keys.map(threadid=>{
    let rowid = `thread${threadid}`;
    $("#gamestate").append(`<div class="thread-row" id="${rowid}"><span class="thread-head">Thread #${threadid}</span></div>`);
    let imgseq = Object.keys(gameObj[threadid]);
    imgseq.sort(function(a, b) {
      return a - b;
    });
    imgseq.map(seqid=>{
      $("#"+rowid).append(`
<div class="row-img-wrapper">
  <div class="img-wrapper">
    <img class="main-img" src="${makeURL(parseInt(gameObj[threadid][seqid]))}"/>
  </div>
  <span class="msg-wrapper">
    <span>
    thrd: ${threadid}, seqNum: ${seqid}, imgId: ${parseInt(gameObj[threadid][seqid])}
    </span>
    <span class="heart-wrapper">
       <span class="chunk threadh">${fromIntToHeart(parseInt(threadid),3).hearts}</span>
       <span class="chunk seqh">${fromIntToHeart(parseInt(seqid),3).hearts}</span>
       <span class="chunk imgh">${fromIntToHeart(parseInt(gameObj[threadid][seqid]),5).hearts}</span>               
    </span>
  </span>
</div>`);
    });
  });
  
  
}

let processData = function(){
  $("#msg").html("");
  let newHTML = "";
  let i = 0;
  data.map(intVal=>{
    if (i == 0){
      newHTML += `<span class="chunk">`
    } else if (i == 3){
      newHTML += `</span><span class="chunk">`;
    } else if (i == 6){
      newHTML += `</span><span class="chunk">`;
    }
    i += 1;
    newHTML += fromDigitToHeart(intVal);
  });
  if (data.length == 0){
    newHTML = "...";
  } else {
    newHTML += "</span>";
  }
  $("#msg").html(newHTML);
  if (data.length == 11){
    addImage(data);
    data = [];
    $("#msg").html("...");
  }
};

$("#red").on("click", function(){
  data.push(0);
  processData();
});

$("#blue").on("click", function(){
  data.push(1);
  processData();
});

$("#yellow").on("click", function(){
  data.push(2);
  processData();
});

$("#resetr").on("click", function(){
  data = [];
  processData();
});

$("#backspace").on("click", function(){
  data.pop();
  processData();
});

initGame();

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js
  2. https://www.gstatic.com/firebasejs/7.14.5/firebase-app.js
  3. https://www.gstatic.com/firebasejs/7.14.5/firebase-database.js