Équilibre dans la conception de jeux de bureau: création de graphiques avec Google App Script et Gephi





Bonjour à tous! Je m'appelle Nikita et je voudrais partager avec vous certains des aspects pratiques du développement de mon jeu de société Ghost Letters (qui sortira ce mois-ci par Economikus). Nous avons essayé d'aborder le processus de développement de manière aussi systémique que possible, afin que notre expérience puisse être intéressante pour quelqu'un.





Ghost Letters est un jeu de société policier avec des rôles secrets pour la déduction, le bluff et la pensée associative. Si vous aimez jouer à "Mafia" ou "Imaginarium" - je suis sûr que vous l'aimerez aussi. Des jeux de société modernes en genre, il est le plus proche de "Mysterium" et "Criminalist".





Des tâches attribuées

La mécanique de base de Ghost Letters est basée sur des associations entre des cartes avec des images de divers objets (cartes de preuve). Et à l'un des premiers stades de développement, nous avons posé la question: "Est-il possible de calculer et de construire un équilibre dans le jeu sur les associations?" En fait, pourquoi ne pas l'essayer.





La tâche d'équilibrer les associations était à peu près la suivante:





  • Réduisez au minimum le nombre d'associations fortes «sans ambiguïté». Chaque carte devrait idéalement être associée à plusieurs autres avec une force à peu près égale.





  • “ ”. , .





  • .





, , . 150 , – . , .





Google Docs

- Google , . - :





  • Google App Script. JS , -.





  • , . , .





  • , . - . .





Gephi

. .





:





. Google Sheets, , .





4 :





0 –

1 – ,

2 –

3 –





, – , .





, 150 150 ( , “”). , . , , , .





, , – , . , .





:





. id , . , id.





//     
function RefreshPictures() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  //  
  var sheet_a = ss.getSheetByName("");
  var range_a = sheet_a.getDataRange();
  //   
  var sheet_p = ss.getSheetByName("");
  var range_p = sheet_p.getDataRange();
  
  //    
  var row = sheet_a.getActiveCell().getRow();
  var col = sheet_a.getActiveCell().getColumn();
  
  //  id 
  var id1 = range_a.getCell(row, 1).getDisplayValue().toString();
  var id2 = range_a.getCell(1, col).getDisplayValue().toString();
  
  //        id
  var pos_pic1 = RowOfId(id1, range_p);
  var pos_pic2 = RowOfId(id2, range_p);
  
  // ,       id
  if (pos_pic1 != -1) {
    //       ,
    //      
    var pic1_f = range_p.getCell(pos_pic1, 2).getFormula();
    range_a.getCell(2, 1).setFormula(pic1_f);
  }
  else
  {
    range_a.getCell(2, 1).setValue("X");
  }
  
  if (pos_pic2 != -1) {
    var pic2_f = range_p.getCell(pos_pic2, 2).getFormula();
    range_a.getCell(2, 2).setFormula(pic2_f);
  }
  else
  {
    range_a.getCell(2, 2).setValue("X");
  }
}

//        id
function RowOfId(id, rng) {  
  var height = rng.getHeight();
  var data = rng.getValues();
  
  for (var i = 1; i < height; i++) {    
    if (data[i][0].toString() == id) {
      return i + 1;
    }
  }
  
  return -1;
}
      
      



. 150 , Google Sheets ( ). -, Google App Script .





//        Google Drive
function LoadPicturesFromDrive() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet_p = ss.getSheetByName("");
  var range_p = sheet_p.getDataRange();
  
  var art_folder = DriveApp.getRootFolder().getFoldersByName("  ").next()
  var files = art_folder.getFiles();
  
  //     
  var i = 1;
  while (files.hasNext()) {
    var file = files.next();
    
    var file_name = file.getName();
    //     id 
    var id = file_name.slice(0, file_name.indexOf("."));
    
    //  id  
    sheet_p.getRange(i + 1, 1).setValue(id);
    
    //     
    file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
    var file_id = file.getId();
    
    //        IMAGE
    sheet_p.getRange(i + 1, 2).setFormula("=IMAGE(\"" + "https://drive.google.com/uc?export=download&id=" + file_id + "\")");
    
    i = i + 1;
  }
}
      
      



, Google Sheets Google Drive, - 10% . , , , . API Dropbox, . Dropbox , , .





//        Dropbox
function LoadPicturesFromDropbox() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet_p = ss.getSheetByName("");
  var range_p = sheet_p.getDataRange();
  
  //    POST-
  var data = {
    "path": "",
    "recursive": false,
    "include_media_info": false,
    "include_deleted": false,
    "include_has_explicit_shared_members": false,
    "include_mounted_folders": true,
    "include_non_downloadable_files": true
  };
  var payload = JSON.stringify(data);
  
  var options = {
    "method" : "POST",
    "contentType" : "application/json",
    "headers" : {
       "Authorization" : "Bearer [ ]"
    },
    "payload" : payload,
    muteHttpExceptions : true
  };
  
  //  POST-      
  var url = "https://api.dropboxapi.com/2/files/list_folder";
  var response = UrlFetchApp.fetch(url, options);
  var json = JSON.parse(response.getContentText());
  
  //    
  for (var i = 0; i < json.entries.length; i++) {
    var name = json.entries[i].name;
    //     
    CreateSharedLink(name);
    var sh_link = GetSharedLink(name);
    
    //     id 
    id = name.slice(0, name.indexOf("."))
  
    //        IMAGE
    sheet_p.getRange(i + 2, 1).setValue(id);
    sheet_p.getRange(i + 2, 2).setFormula("=IMAGE(\"" + sh_link+"\")");
  }
}

//      
function CreateSharedLink(name) {
  //    POST-
  var data = {
    "path": ("/" + name),
    "settings": {
        "requested_visibility": "public",
        "audience": "public",
        "access": "viewer"
    }
  };
  var payload = JSON.stringify(data);
  
  var options = {
    "method" : "POST",
    "contentType" : "application/json",
    "headers" : {
       "Authorization" : "Bearer [ ]"
    },
    "payload" : payload,
    muteHttpExceptions : true
  };
  
  //  POST-      
  var url = "https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings";
  var response = UrlFetchApp.fetch(url, options);
}

//     
function GetSharedLink(name) {
  //    POST-
  var data = {
    "path": ("/" + name)
  };
  var payload = JSON.stringify(data);
  
  var options = {
    "method" : "POST",
    "contentType" : "application/json",
    "headers" : {
       "Authorization" : "Bearer [ ]"
    },
    "payload" : payload,
    muteHttpExceptions : true
  };
  
  //  POST-      
  var url = "https://api.dropboxapi.com/2/sharing/list_shared_links";
  var response = UrlFetchApp.fetch(url, options);
  var json = JSON.parse(response.getContentText());
  
  //       
  var urlForDownload = json.links[0].url.slice(0, -1) + '1';
  
  return urlForDownload;
}
      
      



Gephi ( ) CSV. : (: id, label) (: source, target, weight).





//         
function CreateGraph() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet_a = ss.getSheetByName("");
  var range_a = sheet_a.getDataRange();
  var data = range_a.getValues();
  var height = range_a.getHeight();
  
  //   
  var sheet_lbl = ss.getSheetByName("Graph Labels");
  //    
  var sheet_edg = ss.getSheetByName("Graph Edges");
  
  //     
  var weights = new Array("1", "2", "3");
  var edg_num = 0;
  
  //    
  var lbl_header = ["Id", "Label"];
  //     
  var edg_header = ["Source", "Target", "Weight"];
  
  //  
  sheet_lbl.clear();
  sheet_edg.clear();
  
  //    
  sheet_lbl.appendRow(lbl_header);
  sheet_edg.appendRow(edg_header);
  
  // ,        
  var tmp_arr = [];
  var tmp_arr_len = 0;
  
  //     (  )
  for (var i = 2; i < height; i++) {
    var id1 = data[i][0];
    var name1 = data[i][1];
    
    //      
    var lbl_row = [id1, name1];
    sheet_lbl.appendRow(lbl_row);
    
    for (var j = i + 1; j < height; j++) {
      var wt = data[i][j].toString();
      
      if (weights.includes(wt)) {
        var id2 = data[0][j];
        edg_num += 1;
        
        var edg_row = [id1, id2, wt];
        
        tmp_arr.push(edg_row);
        tmp_arr_len += 1;
        
        //      100 ,     .
        //        ,   
        //     Google App Script
        if (tmp_arr_len >= 100) {
          sheet_edg.getRange(sheet_edg.getLastRow() + 1, 1, tmp_arr_len, 3).setValues(tmp_arr);
          tmp_arr = [];
          tmp_arr_len = 0;
        }
      }
    }
  }
  
  //        
  if (tmp_arr_len > 0) {
    sheet_edg.getRange(sheet_edg.getLastRow() + 1, 1, tmp_arr_len, 3).setValues(tmp_arr);
    tmp_arr = [];
    tmp_arr_len = 0;
  }
}
      
      



Gephi ( ). , , , . .





, , . , “1” , . “2” “3”. .





, , , - . “”, . “” “” Gephi. , 100 :





, , “” . , . “”. , .





Bien sûr, au niveau de la visualisation du graphe et des méthodes de son analyse, il y a encore du travail à faire, mais cette approche a déjà bien fait ses preuves. Si vous avez également utilisé des graphiques dans le développement de vos jeux, il sera très intéressant de connaître votre expérience.






Si vous souhaitez suivre le développement du projet, abonnez-vous au groupe de jeux VKontakte et Instagram . C'est là que je poste des notes de développement, des extraits de tracé, etc.








All Articles