diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6ce8767 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,28 @@ +{ + "workbench.view.alwaysShowHeaderActions": true, + "workbench.tree.renderIndentGuides": "none", + "[pvs]": { + "editor.wordSeparators": "`~!@#$%^&*()-=+[{]}|;:'\",.<>/" + }, + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "**/Thumbs.db": true, + "**/*.???~": true, + "**/.pvscontext": true, + "**/pvsbin": true, + "**/*.jprf": true, + "**/*.prf": true, + "**/orphaned-proofs.prf": true, + "**/*_adt.pvs": false, + "**/*.log": false, + "**/.vscode": true + }, + "files.readonlyInclude": { + "**/*.tccs": true, + "**/*.summary": true + } +} \ No newline at end of file diff --git a/examples/example_1.tscn b/examples/example_1.tscn index 88ce9e3..7d67949 100644 --- a/examples/example_1.tscn +++ b/examples/example_1.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=5 format=3 uid="uid://ddr6qtwy1pesd"] -[ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_6846p"] +[ext_resource type="Script" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_6846p"] [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_s3fct"] [sub_resource type="FastNoiseLite" id="FastNoiseLite_dmk8h"] diff --git a/examples/example_3.tscn b/examples/example_3.tscn index 680ce1e..df2b8a6 100644 --- a/examples/example_3.tscn +++ b/examples/example_3.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=4 format=3 uid="uid://cmucgsppcoo5e"] -[ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_eimw3"] -[ext_resource type="Script" uid="uid://jw3o2qu3a0pl" path="res://examples/LabelStepPass.gd" id="2_4aq4t"] +[ext_resource type="Script" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_eimw3"] +[ext_resource type="Script" path="res://examples/LabelStepPass.gd" id="2_4aq4t"] [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_4upxj"] [node name="CompShadStudioEx3" type="Node2D"] diff --git a/examples/simple_circle/simple_circle.tscn b/examples/simple_circle/simple_circle.tscn index 8c68dd5..3a07914 100644 --- a/examples/simple_circle/simple_circle.tscn +++ b/examples/simple_circle/simple_circle.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=3 format=3 uid="uid://c63rtjh0eurgm"] -[ext_resource type="Script" uid="uid://c8esqdv0y26yp" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_rlapi"] +[ext_resource type="Script" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_rlapi"] [ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_y7slp"] [node name="SimpleCircle" type="Node2D"] diff --git a/examples/territoire/.vscode/settings.json b/examples/territoire/.vscode/settings.json new file mode 100644 index 0000000..a07d7ea --- /dev/null +++ b/examples/territoire/.vscode/settings.json @@ -0,0 +1,27 @@ +{ + "workbench.view.alwaysShowHeaderActions": true, + "workbench.tree.renderIndentGuides": "none", + "[pvs]": { + "editor.wordSeparators": "`~!@#$%^&*()-=+[{]}|;:'\",.<>/" + }, + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/.DS_Store": true, + "**/Thumbs.db": true, + "**/*.???~": true, + "**/.pvscontext": true, + "**/pvsbin": true, + "**/*.jprf": true, + "**/*.prf": true, + "**/orphaned-proofs.prf": true, + "**/*_adt.pvs": false, + "**/*.log": false, + "**/.vscode": true + }, + "files.readonlyInclude": { + "**/*.tccs": true, + "**/*.summary": true + } +} \ No newline at end of file diff --git a/examples/territoire/bonbon.cpp b/examples/territoire/bonbon.cpp new file mode 100644 index 0000000..8036a16 --- /dev/null +++ b/examples/territoire/bonbon.cpp @@ -0,0 +1,464 @@ +#define NEUTRE 0xFF000000 +#define JOUEUR 0xFF0000FF +#define IA_1 0xFF00FF00 +#define IA_2 0xFFFF0000 +#define IA_3 0xFFFFFF00 + +// Couleurs pour afficher les scores +#define SCORE_BG_JOUEUR 0xFFDDDDFF +#define SCORE_BG_IA_1 0xFFDDFFDD +#define SCORE_BG_IA_2 0xFFFFDDDD +#define SCORE_BG_IA_3 0xFFFFFFDD +#define ZONE_DEMILITARISEE 0xFF888888 + +#define AI_EXPANSION_RATE 0.2 + +// Stockage des coordonnees de la souris +#define MOUSE_STORAGE_X 0 +#define MOUSE_STORAGE_Y 1 +#define MOUSE_STORAGE_CLICKED 2 +#define MOUSE_BUTTON_PRESSED 3 + +// Stockage des scores +#define SCORE_JOUEUR 4 +#define SCORE_IA_1 5 +#define SCORE_IA_2 6 +#define SCORE_IA_3 7 + +// Taille de la zone demilitarisee +#define ZONE_DEM_SIZE 30 + +// Verifier si un point est dans la zone demilitarisee +bool is_demilitarized_zone(int x, int y) { + int centerX = int(WSX) / 2; + int centerY = int(WSY) / 2; + + return (x >= centerX - ZONE_DEM_SIZE / 2 && x < centerX + ZONE_DEM_SIZE / 2 && + y >= centerY - ZONE_DEM_SIZE / 2 && y < centerY + ZONE_DEM_SIZE / 2); +} + +int hash(int x, int y, int s) { + int a = x * 0x6A09E667; + int b = y * 0xBB67AE85; + int c = s * 0x3C6EF372; + int h = a + b + c; + h = (h ^ (h >> 16)) * 0x85EBCA6B; + h = (h ^ (h >> 13)) * 0xC2B2AE35; + h = h ^ (h >> 16); + return h; +} + +bool random_event(int x, int y, int step, float probability) { + int random_value = hash(x, y, step); + float normalized = float(random_value) / 2147483647.0; + return (normalized > 0.0 ? normalized : -normalized) < probability; +} + +// Fonction pour calculer et mettre a jour les scores +void calculate_scores() { + if (gl_GlobalInvocationID.x == 0 && gl_GlobalInvocationID.y == 0) { + // Initialiser les scores a zero + data_0[SCORE_JOUEUR] = 0; + data_0[SCORE_IA_1] = 0; + data_0[SCORE_IA_2] = 0; + data_0[SCORE_IA_3] = 0; + + // Parcourir toute la grille pour compter les territoires + for (int i = 0; i < int(WSX) * int(WSY); i++) { + // Ne compter que les pixels qui ne sont pas dans la zone demilitarisee + int px = i % int(WSX); + int py = i / int(WSX); + + if (!is_demilitarized_zone(px, py)) { + if (data_0[i] == JOUEUR) { + data_0[SCORE_JOUEUR]++; + } else if (data_0[i] == IA_1) { + data_0[SCORE_IA_1]++; + } else if (data_0[i] == IA_2) { + data_0[SCORE_IA_2]++; + } else if (data_0[i] == IA_3) { + data_0[SCORE_IA_3]++; + } + } + } + } +} + +// Affiche un chiffre a la position donnee +void draw_digit(int digit, int startX, int startY, int color) { + int x = int(gl_GlobalInvocationID.x); + int y = int(gl_GlobalInvocationID.y); + int p = x + y * int(WSX); + + // Verification si nous sommes dans la region du chiffre + if (x >= startX && x < startX + 3 && y >= startY && y < startY + 5) { + int localX = x - startX; + int localY = y - startY; + bool pixel_on = false; + + // Representation minimaliste des chiffres en 3x5 pixels + switch (digit) { + case 0: + pixel_on = (localX == 0 || localX == 2 || localY == 0 || localY == 4) || + (localX == 1 && (localY == 0 || localY == 4)); + break; + case 1: + pixel_on = (localX == 1) || (localY == 4 && localX != 0); + break; + case 2: + pixel_on = (localY == 0 || localY == 2 || localY == 4) || + (localX == 2 && localY == 1) || (localX == 0 && localY == 3); + break; + case 3: + pixel_on = (localY == 0 || localY == 2 || localY == 4) || (localX == 2); + break; + case 4: + pixel_on = (localX == 0 && localY < 3) || (localX == 2) || (localY == 2); + break; + case 5: + pixel_on = (localY == 0 || localY == 2 || localY == 4) || + (localX == 0 && localY == 1) || (localX == 2 && localY == 3); + break; + case 6: + pixel_on = (localY == 0 || localY == 2 || localY == 4) || + (localX == 0) || (localX == 2 && localY > 2); + break; + case 7: + pixel_on = (localY == 0) || (localX == 2); + break; + case 8: + pixel_on = (localY == 0 || localY == 2 || localY == 4) || + (localX == 0 || localX == 2); + break; + case 9: + pixel_on = (localY == 0 || localY == 2 || localY == 4) || + (localX == 2) || (localX == 0 && localY < 3); + break; + } + + if (pixel_on) { + data_0[p] = color; + } + } +} + +// Fonction pour afficher les scores dans la zone demilitarisee +void draw_scores_in_demilitarized_zone() { + int x = int(gl_GlobalInvocationID.x); + int y = int(gl_GlobalInvocationID.y); + + if (is_demilitarized_zone(x, y)) { + int centerX = int(WSX) / 2; + int centerY = int(WSY) / 2; + int p = x + y * int(WSX); + + // Fond de la zone demilitarisee + data_0[p] = ZONE_DEMILITARISEE; + + // Recuperer les scores et les diviser par 16 + int score_joueur = data_0[SCORE_JOUEUR] / (int(WSX)/8); + int score_ia_1 = data_0[SCORE_IA_1] / (int(WSX)/8); + int score_ia_2 = data_0[SCORE_IA_2] / (int(WSX)/8); + int score_ia_3 = data_0[SCORE_IA_3] / (int(WSX)/8); + + // Quadrant haut gauche - Joueur + if (x < centerX && y < centerY) { + data_0[p] = SCORE_BG_JOUEUR; + + // Afficher le score du joueur + // Calculer les chiffres individuels (jusqu'a 3 chiffres) + int hundreds = score_joueur / 100; + int tens = (score_joueur / 10) % 10; + int ones = score_joueur % 10; + + // Position de depart pour l'affichage des chiffres + int startX = centerX - ZONE_DEM_SIZE / 2 + 3; + int startY = centerY - ZONE_DEM_SIZE / 2 + 3; + + // Afficher chaque chiffre + if (hundreds > 0) { + draw_digit(hundreds, startX, startY, JOUEUR); + draw_digit(tens, startX + 4, startY, JOUEUR); + draw_digit(ones, startX + 8, startY, JOUEUR); + } else if (tens > 0) { + draw_digit(tens, startX + 2, startY, JOUEUR); + draw_digit(ones, startX + 6, startY, JOUEUR); + } else { + draw_digit(ones, startX + 4, startY, JOUEUR); + } + } + // Quadrant haut droite - IA_1 + else if (x >= centerX && y < centerY) { + data_0[p] = SCORE_BG_IA_1; + + // Afficher le score de l'IA 1 + int hundreds = score_ia_1 / 100; + int tens = (score_ia_1 / 10) % 10; + int ones = score_ia_1 % 10; + + int startX = centerX + 3; + int startY = centerY - ZONE_DEM_SIZE / 2 + 3; + + if (hundreds > 0) { + draw_digit(hundreds, startX, startY, IA_1); + draw_digit(tens, startX + 4, startY, IA_1); + draw_digit(ones, startX + 8, startY, IA_1); + } else if (tens > 0) { + draw_digit(tens, startX + 2, startY, IA_1); + draw_digit(ones, startX + 6, startY, IA_1); + } else { + draw_digit(ones, startX + 4, startY, IA_1); + } + } + // Quadrant bas gauche - IA_2 + else if (x < centerX && y >= centerY) { + data_0[p] = SCORE_BG_IA_2; + + // Afficher le score de l'IA 2 + int hundreds = score_ia_2 / 100; + int tens = (score_ia_2 / 10) % 10; + int ones = score_ia_2 % 10; + + int startX = centerX - ZONE_DEM_SIZE / 2 + 3; + int startY = centerY + 3; + + if (hundreds > 0) { + draw_digit(hundreds, startX, startY, IA_2); + draw_digit(tens, startX + 4, startY, IA_2); + draw_digit(ones, startX + 8, startY, IA_2); + } else if (tens > 0) { + draw_digit(tens, startX + 2, startY, IA_2); + draw_digit(ones, startX + 6, startY, IA_2); + } else { + draw_digit(ones, startX + 4, startY, IA_2); + } + } + // Quadrant bas droite - IA_3 + else if (x >= centerX && y >= centerY) { + data_0[p] = SCORE_BG_IA_3; + + // Afficher le score de l'IA 3 + int hundreds = score_ia_3 / 100; + int tens = (score_ia_3 / 10) % 10; + int ones = score_ia_3 % 10; + + int startX = centerX + 3; + int startY = centerY + 3; + + if (hundreds > 0) { + draw_digit(hundreds, startX, startY, IA_3); + draw_digit(tens, startX + 4, startY, IA_3); + draw_digit(ones, startX + 8, startY, IA_3); + } else if (tens > 0) { + draw_digit(tens, startX + 2, startY, IA_3); + draw_digit(ones, startX + 6, startY, IA_3); + } else { + draw_digit(ones, startX + 4, startY, IA_3); + } + } + } +} + +void main() { + int x = int(gl_GlobalInvocationID.x); + int y = int(gl_GlobalInvocationID.y); + int p = x + y * int(WSX); + + // Initialisation + if (step == 0) { + // Verifier si le pixel est dans la zone demilitarisee + if (is_demilitarized_zone(x, y)) { + draw_scores_in_demilitarized_zone(); + } else { + // Initialisation normale pour les autres pixels + if (x < int(WSX)/8 && y < int(WSY)/8) { + data_0[p] = JOUEUR; + } + // IA 1 en haut a droite + else if (x > int(WSX)*7/8 && y < int(WSY)/8) { + data_0[p] = IA_1; + } + // IA 2 en bas a gauche + else if (x < int(WSX)/8 && y > int(WSY)*7/8) { + data_0[p] = IA_2; + } + // IA 3 en bas a droite + else if (x > int(WSX)*7/8 && y > int(WSY)*7/8) { + data_0[p] = IA_3; + } + // Reste de la carte neutre + else { + data_0[p] = NEUTRE; + } + } + + // Initialiser les scores + if (x == 0 && y == 0) { + data_0[SCORE_JOUEUR] = 0; + data_0[SCORE_IA_1] = 0; + data_0[SCORE_IA_2] = 0; + data_0[SCORE_IA_3] = 0; + } + } + // Logique de jeu + else { + // Si on est dans la zone demilitarisee, on affiche les scores + if (is_demilitarized_zone(x, y)) { + draw_scores_in_demilitarized_zone(); + } else { + // Sinon, logique de jeu normale + + // Gestion du clic de souris + bool clicked = false; + + // Verifier si c est un nouveau clic seulement au debut de la frame + if (y == 0 && x == 0) { + int lastX = data_0[MOUSE_STORAGE_X]; + int lastY = data_0[MOUSE_STORAGE_Y]; + bool lastButtonState = data_0[MOUSE_BUTTON_PRESSED] > 0; + bool currentButtonState = mouse_button > 0; + + // Detecter un clic seulement quand le bouton vient d'etre presse + if (currentButtonState && !lastButtonState) { + clicked = true; + data_0[MOUSE_STORAGE_CLICKED] = 1; + } else { + data_0[MOUSE_STORAGE_CLICKED] = 0; + } + + // Sauvegarder la position et l'etat du bouton + data_0[MOUSE_STORAGE_X] = mousex; + data_0[MOUSE_STORAGE_Y] = mousey; + data_0[MOUSE_BUTTON_PRESSED] = currentButtonState ? 1 : 0; + } + + clicked = data_0[MOUSE_STORAGE_CLICKED] == 1; + + // Interaction joueur - expansion par clic + if (clicked && mousex >= 0 && mousey >= 0) { + if (data_0[p] == JOUEUR && random_event(x, y, step, AI_EXPANSION_RATE * 10000000)) { + int direction = hash(x, y, step) % 8; + int dx = 0, dy = 0; + + switch (direction) { + case 0: dx = -1; dy = 0; break; + case 1: dx = 1; dy = 0; break; + case 2: dx = 0; dy = -1; break; + case 3: dx = 0; dy = 1; break; + case 4: dx = -1; dy = -1; break; + case 5: dx = 1; dy = -1; break; + case 6: dx = -1; dy = 1; break; + case 7: dx = 1; dy = 1; break; + } + + int nx = x + dx; + int ny = y + dy; + + // Verifier que le pixel voisin n'est pas dans la zone demilitarisee + if (nx >= 0 && nx < int(WSX) && ny >= 0 && ny < int(WSY) && !is_demilitarized_zone(nx, ny)) { + int voisin_p = nx + ny * int(WSX); + + if (data_0[voisin_p] == NEUTRE || (data_0[voisin_p] != JOUEUR && random_event(x, y, step, 3))) { + data_0[voisin_p] = JOUEUR; + } + } + } + } + + // IA expansion automatique - garde ce comportement pour equilibrer le jeu + if (step % 5 == 0) { // Ralentir IA pour equilibrer + // IA 1 Plus agressive + if (data_0[p] == IA_1 && random_event(x, y, step, AI_EXPANSION_RATE * 1.5)) { + int direction = hash(x, y, step) % 8; + int dx = 0, dy = 0; + + switch (direction) { + case 0: dx = -1; dy = 0; break; + case 1: dx = 1; dy = 0; break; + case 2: dx = 0; dy = -1; break; + case 3: dx = 0; dy = 1; break; + case 4: dx = -1; dy = -1; break; + case 5: dx = 1; dy = -1; break; + case 6: dx = -1; dy = 1; break; + case 7: dx = 1; dy = 1; break; + } + + int nx = x + dx; + int ny = y + dy; + + // Verifier que le pixel voisin n'est pas dans la zone demilitarisee + if (nx >= 0 && nx < int(WSX) && ny >= 0 && ny < int(WSY) && !is_demilitarized_zone(nx, ny)) { + int voisin_p = nx + ny * int(WSX); + + if (data_0[voisin_p] == NEUTRE || (data_0[voisin_p] != IA_1 && random_event(x, y, step, 0.2))) { + data_0[voisin_p] = IA_1; + } + } + } + + // IA 2 Expansion normale + else if (data_0[p] == IA_2 && random_event(x, y, step, AI_EXPANSION_RATE)) { + int direction = hash(x, y, step) % 8; + int dx = 0, dy = 0; + + switch (direction) { + case 0: dx = -1; dy = 0; break; + case 1: dx = 1; dy = 0; break; + case 2: dx = 0; dy = -1; break; + case 3: dx = 0; dy = 1; break; + case 4: dx = -1; dy = -1; break; + case 5: dx = 1; dy = -1; break; + case 6: dx = -1; dy = 1; break; + case 7: dx = 1; dy = 1; break; + } + + int nx = x + dx; + int ny = y + dy; + + // Verifier que le pixel voisin n'est pas dans la zone demilitarisee + if (nx >= 0 && nx < int(WSX) && ny >= 0 && ny < int(WSY) && !is_demilitarized_zone(nx, ny)) { + int voisin_p = nx + ny * int(WSX); + + if (data_0[voisin_p] == NEUTRE || (data_0[voisin_p] != IA_2 && random_event(x, y, step, 0.2))) { + data_0[voisin_p] = IA_2; + } + } + } + + // IA 3 Plus defensive + else if (data_0[p] == IA_3 && random_event(x, y, step, AI_EXPANSION_RATE * 0.8)) { + int direction = hash(x, y, step) % 8; + int dx = 0, dy = 0; + + switch (direction) { + case 0: dx = -1; dy = 0; break; + case 1: dx = 1; dy = 0; break; + case 2: dx = 0; dy = -1; break; + case 3: dx = 0; dy = 1; break; + case 4: dx = -1; dy = -1; break; + case 5: dx = 1; dy = -1; break; + case 6: dx = -1; dy = 1; break; + case 7: dx = 1; dy = 1; break; + } + + int nx = x + dx; + int ny = y + dy; + + // Verifier que le pixel voisin n'est pas dans la zone demilitarisee + if (nx >= 0 && nx < int(WSX) && ny >= 0 && ny < int(WSY) && !is_demilitarized_zone(nx, ny)) { + int voisin_p = nx + ny * int(WSX); + + if (data_0[voisin_p] == NEUTRE || (data_0[voisin_p] != IA_3 && random_event(x, y, step, 0.2))) { + data_0[voisin_p] = IA_3; + } + } + } + } + } + + // Calculer les scores tous les steps + if (step % 30 == 0 && x == 0 && y == 0) { + calculate_scores(); + } + } +} \ No newline at end of file diff --git a/examples/territoire/envahir.cpp b/examples/territoire/envahir.cpp new file mode 100644 index 0000000..55a5d09 --- /dev/null +++ b/examples/territoire/envahir.cpp @@ -0,0 +1,149 @@ +// DEFINITIONS DES COULEURS ET ÉTATS +#define VIDE 0 +#define JOUEUR 0xFF0000FF // Rouge - Joueur humain +#define IA_1 0xFF00FF00 // Vert - IA 1 +#define IA_2 0xFFFF0000 // Bleu - IA 2 +#define IA_3 0xFFFFFF00 // Jaune - IA 3 + +// PARAMÈTRES DU JEU +#define FORCE_EXPANSION 0.15 // Probabilité d'expansion (0-1) +#define FORCE_CONQUETE 0.08 // Probabilité de conquête d'un territoire adverse (0-1) + +// Fonction pseudo-aléatoire basée sur les coordonnées et le step +int hash(int x, int y, int s) { + int a = x * 0x6A09E667; + int b = y * 0xBB67AE85; + int c = s * 0x3C6EF372; + int h = a + b + c; + h = (h ^ (h >> 16)) * 0x85EBCA6B; + h = (h ^ (h >> 13)) * 0xC2B2AE35; + h = h ^ (h >> 16); + return h; +} + +// Fonction pour déterminer si un événement aléatoire se produit +bool random_event(int x, int y, int step, float probability) { + int random_value = hash(x, y, step); + return (float(random_value) / 2147483647.0) < probability; +} + +void main() { + // Récupération des coordonnées + int x = int(gl_GlobalInvocationID.x); + int y = int(gl_GlobalInvocationID.y); + int p = x + y * int(WSX); + + if (step == 0) { + // INITIALISATION: PLACER LES TERRITOIRES DE DÉPART + if (x < int(WSX)/6 && y < int(WSY)/6) { + // Territoire du joueur (en haut à gauche) + data_0[p] = JOUEUR; + } else if (x > int(WSX)*5/6 && y < int(WSY)/6) { + // IA 1 (en haut à droite) + data_0[p] = IA_1; + } else if (x < int(WSX)/6 && y > int(WSY)*5/6) { + // IA 2 (en bas à gauche) + data_0[p] = IA_2; + } else if (x > int(WSX)*5/6 && y > int(WSY)*5/6) { + // IA 3 (en bas à droite) + data_0[p] = IA_3; + } else { + // Reste de la carte vide + data_0[p] = VIDE; + } + } else { + // LOGIQUE D'EXPANSION ET DE CONQUÊTE + int couleur_actuelle = data_0[p]; + + // SI LA CELLULE A UN PROPRIÉTAIRE + if (couleur_actuelle != VIDE) { + // TENTER D'ÉTENDRE LE TERRITOIRE + if (random_event(x, y, step, FORCE_EXPANSION)) { + // Choisir une direction aléatoire + int direction = hash(x, y, step) % 8; + int dx = 0, dy = 0; + + // 8 directions possibles + switch (direction) { + case 0: dx = -1; dy = 0; break; // Gauche + case 1: dx = 1; dy = 0; break; // Droite + case 2: dx = 0; dy = -1; break; // Haut + case 3: dx = 0; dy = 1; break; // Bas + case 4: dx = -1; dy = -1; break; // Diagonale haut-gauche + case 5: dx = 1; dy = -1; break; // Diagonale haut-droite + case 6: dx = -1; dy = 1; break; // Diagonale bas-gauche + case 7: dx = 1; dy = 1; break; // Diagonale bas-droite + } + + // Calculer les nouvelles coordonnées + int nx = x + dx; + int ny = y + dy; + + // Vérifier les limites + if (nx >= 0 && nx < int(WSX) && ny >= 0 && ny < int(WSY)) { + int voisin_p = nx + ny * int(WSX); + int couleur_voisin = data_0[voisin_p]; + + // Si le voisin est vide, l'occuper + if (couleur_voisin == VIDE) { + data_0[voisin_p] = couleur_actuelle; + } + // Si le voisin appartient à un autre joueur, tenter de le conquérir + else if (couleur_voisin != couleur_actuelle && random_event(x, y, step, FORCE_CONQUETE)) { + data_0[voisin_p] = couleur_actuelle; + } + } + } + } + // SI LA CELLULE EST VIDE + else { + // COMPTAGE DES VOISINS PAR COULEUR + int nb_voisins_joueur = 0; + int nb_voisins_ia1 = 0; + int nb_voisins_ia2 = 0; + int nb_voisins_ia3 = 0; + + // Parcours des 8 cellules voisines + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) { + if (!(dx == 0 && dy == 0)) { + int nx = x + dx; + int ny = y + dy; + + if (nx >= 0 && nx < int(WSX) && ny >= 0 && ny < int(WSY)) { + int voisin = data_0[nx + ny * int(WSX)]; + if (voisin == JOUEUR) nb_voisins_joueur++; + else if (voisin == IA_1) nb_voisins_ia1++; + else if (voisin == IA_2) nb_voisins_ia2++; + else if (voisin == IA_3) nb_voisins_ia3++; + } + } + } + } + + // DÉTERMINER LE PROPRIÉTAIRE MAJORITAIRE PARMI LES VOISINS + int max_voisins = max(nb_voisins_joueur, max(nb_voisins_ia1, max(nb_voisins_ia2, nb_voisins_ia3))); + + // S'il y a au moins 2 voisins de la même couleur, coloniser + if (max_voisins >= 2) { + if (max_voisins == nb_voisins_joueur) data_0[p] = JOUEUR; + else if (max_voisins == nb_voisins_ia1) data_0[p] = IA_1; + else if (max_voisins == nb_voisins_ia2) data_0[p] = IA_2; + else if (max_voisins == nb_voisins_ia3) data_0[p] = IA_3; + } + + // INTERACTION AVEC LA SOURIS (SIMULATION) + // À chaque multiple de 100 steps, simuler un clic dans une zone aléatoire pour l'expansion du joueur + if (step % 100 == 0) { + // Calculer une position où le joueur "clique" + int click_x = (hash(0, step, 0) % int(WSX)); + int click_y = (hash(step, 0, 0) % int(WSY)); + + // Si nous sommes près de la position du "clic" et à proximité d'un territoire du joueur + if (abs(x - click_x) < 5 && abs(y - click_y) < 5 && nb_voisins_joueur > 0) { + data_0[p] = JOUEUR; // Conquête par le joueur + } + } + } + } +} \ No newline at end of file diff --git a/examples/territoire/sceneTerritoire.tscn b/examples/territoire/sceneTerritoire.tscn new file mode 100644 index 0000000..a6ea8aa --- /dev/null +++ b/examples/territoire/sceneTerritoire.tscn @@ -0,0 +1,34 @@ +[gd_scene load_steps=3 format=3 uid="uid://cyk1jgtevq1pg"] + +[ext_resource type="Script" path="res://addons/compute_shader_studio/compute_shader_studio_2d.gd" id="1_fuy1w"] +[ext_resource type="Texture2D" uid="uid://demftcowdd5c6" path="res://examples/icon.svg" id="2_otyot"] + +[node name="T1" type="Node2D"] + +[node name="ComputeShaderStudio2D" type="Node" parent="." node_paths=PackedStringArray("data")] +script = ExtResource("1_fuy1w") +glsl_file = "res://examples/territoire/bonbon.cpp" +data = [NodePath("../Icon")] + +[node name="Icon" type="Sprite2D" parent="."] +texture_filter = 1 +position = Vector2(574, 285.5) +scale = Vector2(8.79688, 4.28906) +texture = ExtResource("2_otyot") + +[node name="Button" type="Button" parent="."] +offset_left = 459.0 +offset_top = 578.0 +offset_right = 559.0 +offset_bottom = 618.0 +text = "Start" + +[node name="Button2" type="Button" parent="."] +offset_left = 602.0 +offset_top = 578.0 +offset_right = 687.0 +offset_bottom = 618.0 +text = "Pause" + +[connection signal="pressed" from="Button" to="ComputeShaderStudio2D" method="_on_button_play"] +[connection signal="pressed" from="Button2" to="ComputeShaderStudio2D" method="_on_button_step"] diff --git a/examples/territoire/teri_test1.cpp b/examples/territoire/teri_test1.cpp new file mode 100644 index 0000000..9803efd --- /dev/null +++ b/examples/territoire/teri_test1.cpp @@ -0,0 +1,96 @@ +#define MORT 0 +#define VIVANT 0xFFFFFFFF +#define Rouge 0xFF0000FF +#define Vert 0xFF00FF00 +#define Bleu 0xFFFF0000 +#define Jaune 0xFFFFFF00 + + +#define MIN_VOISINS_SURVIE 1 +#define MAX_VOISINS_SURVIE 2 +#define VOISINS_NAISSANCE 1 + + +void compute_next_step(uint x, uint y, uint p){ + + int nb_voisins_V = 0; + int nb_voisins_R = 0; + int nb_voisins_J = 0; + int nb_voisins_B = 0; + int nb_voisins_G = 0; + + // Comptage des voisins vivants + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) { + if (!(dx == 0 && dy == 0)) { + int nx = int(x) + dx; + int ny = int(y) + dy; + + if (nx >= 0 && nx < WSX && ny >= 0 && ny < WSY) { + if (data_0[uint(nx + ny * WSX)] == VIVANT) { + nb_voisins_V++; + } + if (data_0[uint(nx + ny * WSX)] == Rouge) { + nb_voisins_R++; + } + if (data_0[uint(nx + ny * WSX)] == Vert) { + nb_voisins_G++; + } + if (data_0[uint(nx + ny * WSX)] == Bleu) { + nb_voisins_B++; + } + if (data_0[uint(nx + ny * WSX)] == Jaune) { + nb_voisins_J++; + } + } + } + } + } + + // La cellule morte devient vivante si elle a 1 voisin vivant + if (data_0[p] == MORT) { + if (nb_voisins_V >= 1) { + data_1[p] = VIVANT; + } else if(nb_voisins_R >= 1){ + data_1[p] = Rouge; + }else if(nb_voisins_J >= 1){ + data_1[p] = Jaune; + }else if(nb_voisins_G >= 1){ + data_1[p] = Vert; + }else if(nb_voisins_B >= 1){ + data_1[p] = Bleu; + } + } +} + + +void main() { + uint x = gl_GlobalInvocationID.x; + uint y = gl_GlobalInvocationID.y; + uint p = x + y * WSX; + + if (step == 0) { + if ( current_pass == 0 ) { + data_0[p] = MORT ; + if (x == WSX / 2 && y == WSY / 2) { // On place une cellule vivante au centre + data_0[p] = VIVANT; + } else if (x == 1 && y== 1){ + data_0[p] = Rouge; + }else if(x == WSX-1 && y== 1){ + data_0[p] = Vert; + }else if(x == 1 && y== WSY-1){ + data_0[p] = Bleu; + }else if(x == WSX-1 && y== WSY-1){ + data_0[p] = Jaune; + } else { + data_0[p] = MORT; + } + } + + } else { + if (current_pass == 0) + compute_next_step(x, y, p); + else + data_0[p] = data_1[p]; // The future is now + } +} diff --git a/examples/territoire/territoire.cpp b/examples/territoire/territoire.cpp new file mode 100644 index 0000000..119fe7f --- /dev/null +++ b/examples/territoire/territoire.cpp @@ -0,0 +1,60 @@ +#define MORT 0 +#define VIVANT 0xFFFFFFFF + +#define MIN_VOISINS_SURVIE 1 +#define MAX_VOISINS_SURVIE 2 +#define VOISINS_NAISSANCE 1 + + +uint hash(uint x, uint y) { + uint a = x * 0x6A09E667; + uint b = y * 0xBB67AE85; + uint h = a + b; + h = (h ^ (h >> 16)) * 0x85EBCA6B; + h = (h ^ (h >> 13)) * 0xC2B2AE35; + h = h ^ (h >> 16); + return h; +} + +void main() { + uint x = gl_GlobalInvocationID.x; + uint y = gl_GlobalInvocationID.y; + uint p = x + y * WSX; + + if (step == 0) { + if (data_0[p] < 0) { + if (x == WSX / 2 && y == WSY / 2) { // On place une cellule vivante au centre + data_0[p] = VIVANT; + } else { + data_0[p] = MORT; + } + } else { + data_0[p] = MORT; + } + } else { + int nb_voisins = 0; + + // Comptage des voisins vivants + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) { + if (!(dx == 0 && dy == 0)) { + int nx = int(x) + dx; + int ny = int(y) + dy; + + if (nx >= 0 && nx < WSX && ny >= 0 && ny < WSY) { + if (data_0[uint(nx + ny * WSX)] == VIVANT) { + nb_voisins++; + } + } + } + } + } + + // La cellule morte devient vivante si elle a 1 voisin vivant + if (data_0[p] == MORT) { + if (nb_voisins >= VOISINS_NAISSANCE) { + data_0[p] = VIVANT; + } + } + } +} diff --git a/examples/territoire/territorial/.vscode/settings.json b/examples/territoire/territorial/.vscode/settings.json new file mode 100644 index 0000000..a07d7ea --- /dev/null +++ b/examples/territoire/territorial/.vscode/settings.json @@ -0,0 +1,27 @@ +{ + "workbench.view.alwaysShowHeaderActions": true, + "workbench.tree.renderIndentGuides": "none", + "[pvs]": { + "editor.wordSeparators": "`~!@#$%^&*()-=+[{]}|;:'\",.<>/" + }, + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/.DS_Store": true, + "**/Thumbs.db": true, + "**/*.???~": true, + "**/.pvscontext": true, + "**/pvsbin": true, + "**/*.jprf": true, + "**/*.prf": true, + "**/orphaned-proofs.prf": true, + "**/*_adt.pvs": false, + "**/*.log": false, + "**/.vscode": true + }, + "files.readonlyInclude": { + "**/*.tccs": true, + "**/*.summary": true + } +} \ No newline at end of file diff --git a/project.godot b/project.godot index 1617345..725be9c 100644 --- a/project.godot +++ b/project.godot @@ -12,7 +12,7 @@ config_version=5 config/name="compute_shader_studio" run/main_scene="res://examples/example_1.tscn" -config/features=PackedStringArray("4.4", "Forward Plus") +config/features=PackedStringArray("4.3", "Forward Plus") config/icon="res://addons/compute_shader_studio/icon.png" [editor_plugins]