]> vgcfreebox.myrthtech.pt Git - ue-aai-hiddenarkgame.git/blob - HiddenArkGame.py
final project of Applied Artificial Inteligence Course
[ue-aai-hiddenarkgame.git] / HiddenArkGame.py
1 import tkinter as tk
2 import sys
3 from tkinter import ttk, messagebox
4 import random
5 import numpy as np
6 from scipy.spatial.distance import cityblock
7
8 # Configurações do tabuleiro (tamanho (X,Y), orçamento inicial, Custo dos sensores)
9 if len(sys.argv) > 1:
10 ROWS = int(sys.argv[1])
11 COLS = int(sys.argv[2])
12 else:
13 ROWS,COLS = 5,5
14
15
16 class TreasureGame:
17 def __init__(self, root):
18 self.root = root
19 self.root.title("Caçadores da Arca Perdida")
20
21 # Inicialização de variáveis de estado
22 self.perfect_mode = False
23 self.INITIAL_BUDGET = 100
24 self.SENSOR_COSTS = {"GPR": 5, "MAG": 3, "VIS": 1, "DIG": 0}
25
26 self.budget = self.INITIAL_BUDGET
27 self.ark_loc = (random.randint(0, ROWS - 1), random.randint(0, COLS - 1))
28 #self.ark_loc = (1,1)
29 self.probs = np.full((ROWS, COLS), 1.0 / (ROWS * COLS))
30 self.used_sensors = set()
31 self.cell_history = {}
32 self.show_treasure_debug = False
33 # Tabelas de Probabilidade Normais
34 self.SENSOR_TABLES = {
35 "GPR": {
36 "STRONG": [0.75, 0.10, 0.05, 0.02],
37 "MODERATE": [0.15, 0.70, 0.15, 0.08],
38 "WEAK": [0.07, 0.15, 0.65, 0.15],
39 "NONE": [0.03, 0.05, 0.15, 0.75]
40 },
41 "MAG": {
42 "HIGH": [0.70, 0.15, 0.10],
43 "MEDIUM": [0.20, 0.65, 0.25],
44 "LOW": [0.10, 0.20, 0.65]
45 },
46 "VIS": {
47 "SUSPICIOUS": [0.60, 0.20],
48 "NORMAL": [0.40, 0.80]
49 }
50 }
51 # Tabelas de Probabilidade Perfeitas
52 self.SENSOR_TABLES_PERFECT = {
53 "GPR": {
54 "STRONG": [1, 0, 0, 0],
55 "MODERATE": [0, 1, 0, 0],
56 "WEAK": [0, 0, 1, 0],
57 "NONE": [0, 0, 0, 1],
58 },
59 "MAG": {
60 "HIGH": [1, 0, 0],
61 "MEDIUM": [0, 1, 0],
62 "LOW": [0, 0, 1]
63 },
64 "VIS": {
65 "SUSPICIOUS": [1, 0],
66 "NORMAL": [0, 1]
67 }
68 }
69
70
71 # Estilos dos botões
72 self.style = ttk.Style()
73 self.style.configure("Perfect.TButton", foreground="green", font=("Arial", 9, "bold"))
74 self.style.configure("Normal.TButton", foreground="black")
75
76 self.setup_ui()
77
78 # Define os sensores utilizados
79 @property
80 def current_sensor_table(self):
81 return self.SENSOR_TABLES_PERFECT if self.perfect_mode else self.SENSOR_TABLES
82
83 def setup_ui(self):
84 # Tabuleiro INICIO
85 self.main_frame = ttk.Frame(self.root, padding="10")
86 self.main_frame.grid(row=0, column=0, sticky="nsew")
87
88 self.buttons = {}
89 for r in range(ROWS):
90 for c in range(COLS):
91 btn = tk.Button(self.main_frame, text="", width=15, height=5,
92 font=("Arial", 8), relief="groove",
93 command=lambda row=r, col=c: self.cell_click(row, col))
94 btn.grid(row=r, column=c, padx=2, pady=2, sticky="nsew")
95 self.buttons[(r, c)] = btn
96 # Tabuleiro FIM
97 # Menu Lateral INICIO
98 control_panel = ttk.Frame(self.root, padding="10")
99 control_panel.grid(row=0, column=1, sticky="ns")
100
101 # Texto Orçamento
102 self.budget_var = tk.StringVar(value=f"Orçamento: {self.budget}")
103 ttk.Label(control_panel, textvariable=self.budget_var, font=("Arial", 12, "bold")).pack(pady=5)
104
105 # RadioButton Sensores
106 self.sensor_var = tk.StringVar(value="GPR")
107 sf = ttk.LabelFrame(control_panel, text=" Selecione o Sensor ", padding="5")
108 sf.pack(fill='x', pady=5)
109 for s, cost in self.SENSOR_COSTS.items():
110 ttk.Radiobutton(sf, text=f"{s} ({cost})", variable=self.sensor_var, value=s).pack(anchor='w')
111
112 # Botão Revelar tesouro
113 self.reveal_btn = ttk.Button(control_panel, text="Revelar Tesouro: OFF", command=self.toggle_treasure,
114 style="Normal.TButton")
115 self.reveal_btn.pack(fill='x', pady=10)
116
117 # Botão Sensores Perfeitos
118 self.perfect_btn = ttk.Button(control_panel, text="Sensores Perfeitos: OFF", command=self.toggle_perfect_mode,
119 style="Normal.TButton")
120 self.perfect_btn.pack(fill='x', pady=5)
121
122 # Painel Histórico
123 ttk.Label(control_panel, text="Histórico:").pack(anchor='w', pady=(10, 0))
124 self.history_log = tk.Text(control_panel, width=30, height=12, state='disabled', font=("Consolas", 9))
125 self.history_log.pack(pady=5)
126
127 # Botão Reiniciar
128 ttk.Button(control_panel, text="Reiniciar Jogo", command=self.reset_game).pack(fill='x', pady=2)
129
130 # Botão Sair
131 ttk.Button(control_panel, text="Sair", command=self.root.destroy).pack(fill='x', pady=5)
132
133 # Menu lateral FIM
134 self.update_grid_display()
135
136 # Activa/Desactiva sensores perfeitos
137 def toggle_perfect_mode(self):
138 self.perfect_mode = not self.perfect_mode
139 if self.perfect_mode:
140 self.perfect_btn.config(text="Sensores Perfeitos: ON", style="Perfect.TButton")
141 else:
142 self.perfect_btn.config(text="Sensores Perfeitos: OFF", style="Normal.TButton")
143
144 self.history_log.config(state='normal')
145 self.history_log.insert(tk.END, f"> Modo Sensores Perfeitos: {'ON' if self.perfect_mode else 'OFF'}\n")
146 self.history_log.see(tk.END)
147 self.history_log.config(state='disabled')
148
149 # Activa/Desactiva localização do tesouro
150 def toggle_treasure(self):
151
152 self.show_treasure_debug = not self.show_treasure_debug
153 if self.show_treasure_debug:
154 self.reveal_btn.config(text="Revelar Tesouro: ON", style="Perfect.TButton")
155 else:
156 self.reveal_btn.config(text="Revelar Tesouro: OFF", style="Normal.TButton")
157
158 self.history_log.config(state='normal')
159 self.history_log.insert(tk.END, f"> Modo Tesouro Visivel: {'ON' if self.show_treasure_debug else 'OFF'}\n")
160 self.history_log.see(tk.END)
161 self.history_log.config(state='disabled')
162
163 self.update_grid_display()
164
165 # Actualiza o tabuleiro
166 def update_grid_display(self):
167 for (r, c), btn in self.buttons.items():
168 treasure_header = "!!! TESOURO !!!\n" if (self.show_treasure_debug and (r, c) == self.ark_loc) else ""
169 p_text = f"{self.probs[r, c]:.1%}"
170 history_text = self.cell_history.get((r, c), "")
171 if history_text:
172 history_text = "\n" + history_text
173
174 btn.config(text=f"{treasure_header}{p_text}{history_text}")
175
176 # Cor de fundo baseada na probabilidade
177 intensity = int(255 - (self.probs[r, c] * 350))
178 intensity = max(min(intensity, 255), 180)
179 color = f"#{255:02x}{intensity:02x}{intensity:02x}"
180
181 if self.show_treasure_debug and (r, c) == self.ark_loc:
182 color = "#FFD700" # Dourado
183
184 btn.config(bg=color)
185
186 # Click das localizações do mapa
187 def cell_click(self, r, c):
188 sensor = self.sensor_var.get()
189 cost = self.SENSOR_COSTS[sensor]
190
191 # Verifica o se sensor já foi usado na localização
192 if sensor != "DIG" and (r, c, sensor) in self.used_sensors:
193 messagebox.showwarning("Aviso", "Este sensor já foi usado aqui!")
194 return
195
196 # Verifica se há orçamento
197 if self.budget < cost:
198 messagebox.showwarning("Aviso", "Orçamento insuficiente!")
199 return
200
201 # Se o jogador utilizou "DIG" verifica se ganhou ou perdeu
202 if sensor == "DIG":
203 if (r, c) == self.ark_loc:
204 messagebox.showinfo("Vitória", f"Encontrou o tesouro!\nPontuação final: {self.budget}")
205 else:
206 messagebox.showerror("Derrota", f"O tesouro não está nesta localização.\nPerdeu o jogo!")
207 self.root.destroy()
208 return
209
210 # Executa o sensor, com base na tabela utilizada
211 dist = cityblock((r, c), self.ark_loc)
212 active_table = self.current_sensor_table[sensor]
213
214 # Determinar leitura baseada nas probabilidades
215 outcomes = list(active_table.keys())
216 max_idx = len(next(iter(active_table.values()))) - 1
217 idx = min(dist, max_idx)
218 weights = [active_table[out][idx] for out in outcomes]
219 print(weights)
220 reading = random.choices(outcomes, weights=weights, k=1)[0]
221
222
223 # Actualiza orçamento
224 self.used_sensors.add((r, c, sensor))
225 self.budget -= cost
226 self.budget_var.set(f"Orçamento: {self.budget}")
227
228 # Atualiza o resultado na localização
229 entry = f"{sensor}:{reading}"
230
231 # Actualiza o histórico
232 self.cell_history[(r, c)] = f"{self.cell_history.get((r, c), '')}\n{entry}".strip()
233 self.history_log.config(state='normal')
234 self.history_log.insert(tk.END, f"{sensor} em ({r},{c}): {reading}\n")
235 self.history_log.see(tk.END)
236 self.history_log.config(state='disabled')
237
238 self.update_probabilities(sensor, reading, (r, c))
239 self.update_grid_display()
240
241 # actualiza as probabilidades no tabuleiro
242 def update_probabilities(self, sensor, reading, click_loc):
243 new_probs = np.zeros((ROWS, COLS))
244 table_row = self.current_sensor_table[sensor][reading]
245 max_idx = len(table_row) - 1
246
247 for r in range(ROWS):
248 for c in range(COLS):
249 d = cityblock((r, c), click_loc)
250 prob_evidence = table_row[min(d, max_idx)]
251 new_probs[r, c] = self.probs[r, c] * prob_evidence
252
253 total = np.sum(new_probs)
254 if total > 0:
255 self.probs = new_probs / total
256
257 # Reinicia o jogo
258 def reset_game(self):
259 self.budget = self.INITIAL_BUDGET
260 self.budget_var.set(f"Orçamento: {self.budget}")
261 self.ark_loc = (random.randint(0, ROWS - 1), random.randint(0, COLS - 1))
262 self.probs = np.full((ROWS, COLS), 1.0 / (ROWS * COLS))
263 self.used_sensors = set()
264 self.cell_history = {}
265
266 self.history_log.config(state='normal')
267 self.history_log.delete('1.0', tk.END)
268 self.history_log.config(state='disabled')
269
270 self.update_grid_display()
271
272
273 if __name__ == "__main__":
274 root = tk.Tk()
275 game = TreasureGame(root)
276 root.mainloop()