Equilíbrio no design de jogos para desktop: construção de gráficos com Google App Script e Gephi





Olá a todos! Meu nome é Nikita, e gostaria de compartilhar com vocês alguns dos aspectos práticos do desenvolvimento do meu jogo de tabuleiro Ghost Letters (a ser lançado este mês pela Economics). Tentamos abordar o processo de desenvolvimento o mais sistematicamente possível, para que nossa experiência seja interessante para alguém.





Ghost Letters é um jogo de tabuleiro de detetive com papéis secretos para dedução, blefe e pensamento associativo. Se você gosta de jogar "Mafia" ou "Imaginarium", tenho certeza que também gostará. Dos jogos de tabuleiro modernos no gênero, é o mais próximo de "Mysterium" e "Criminalista".





Tarefas atribuĂ­das

A mecânica básica de Ghost Letters é baseada em associações entre cartas com imagens de vários objetos (cartas de evidência). E em um dos primeiros estágios de desenvolvimento, fizemos a pergunta: "É possível calcular e construir um equilíbrio no jogo nas associações?" Na verdade, por que não tentar?





A tarefa de equilibrar associações era mais ou menos assim:





  • Minimize o nĂşmero de associações fortes “nĂŁo ambĂ­guas”. Cada carta deve ser associada idealmente a várias outras com força aproximadamente igual.





  • “ ”. , .





  • .





, , . 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 :





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





Claro que, em termos de visualização do gráfico e métodos de sua análise, ainda há trabalho a ser feito, mas esta abordagem já se mostrou bem. Se você também utilizou gráficos no desenvolvimento de seus jogos, será muito interessante saber sobre sua experiência.






Se você estiver interessado em acompanhar o desenvolvimento do projeto, inscreva-se no grupo de jogos VKontakte e Instagram . É aqui que posto notas de desenvolvimento, fragmentos de enredo e muito mais.








All Articles