¡Hola a todos! Mi nombre es Nikita y me gustaría compartir con ustedes algunos de los aspectos prácticos del desarrollo de mi juego de mesa Ghost Letters (que será lanzado este mes por Economics). Intentamos abordar el proceso de desarrollo de la manera más sistemática posible, por lo que nuestra experiencia puede ser interesante para alguien.
Ghost Letters es un juego de mesa de detectives con roles secretos para la deducción, el engaño y el pensamiento asociativo. Si te gusta jugar a "Mafia" o "Imaginarium", estoy seguro de que también te gustará. De los juegos de mesa modernos en el género es lo más cercano a "Mysterium" y "Criminalist".
Tareas asignadas
La mecánica básica de Ghost Letters se basa en asociaciones entre tarjetas con imágenes de varios objetos (tarjetas de evidencia). Y en una de las primeras etapas de desarrollo, hicimos la pregunta: "¿Es posible calcular y construir un equilibrio en el juego sobre asociaciones?" De hecho, ¿por qué no intentarlo?
La tarea de equilibrar las asociaciones fue aproximadamente la siguiente:
Minimice el número de asociaciones fuertes "inequívocas". Idealmente, cada carta debería estar asociada con varias otras con aproximadamente la misma fuerza.
“ ”. , .
.
, , . 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;
}
}
, , . , “1” , . “2” “3”. .
, , , - . “”, . “” “” Gephi. , 100 :
, , “” . , . “”. , .
Por supuesto, en términos de visualización del gráfico y métodos de análisis, todavía queda trabajo por hacer, pero este enfoque ya se ha mostrado bien. Si también usaste gráficos en el desarrollo de tus juegos, será muy interesante conocer tu experiencia.
Si está interesado en seguir el desarrollo del proyecto, suscríbase al grupo de juegos VKontakte e Instagram . Aquí es donde publico notas de desarrollo, fragmentos de la trama y más.