From f38a6bf034889453bd933edf8ea9ba29e6d00773 Mon Sep 17 00:00:00 2001 From: Safak Date: Sat, 28 Jun 2025 20:56:09 +0200 Subject: [PATCH] =?UTF-8?q?P3=20=C3=BCberarbeitet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- P3/Task/Propositions.py | 284 ++++++++++++ P3/Task/Propositions2.py | 411 ++++++++++++++++++ .../__pycache__/Propositions.cpython-310.pyc | Bin 0 -> 7617 bytes .../__pycache__/Propositions2.cpython-310.pyc | Bin 0 -> 10181 bytes P3/Wumpus_Test.py | 0 P3/ai_gym | 1 + P3/main.py | 75 ++++ 7 files changed, 771 insertions(+) create mode 100644 P3/Task/Propositions.py create mode 100644 P3/Task/Propositions2.py create mode 100644 P3/Task/__pycache__/Propositions.cpython-310.pyc create mode 100644 P3/Task/__pycache__/Propositions2.cpython-310.pyc delete mode 100644 P3/Wumpus_Test.py create mode 160000 P3/ai_gym create mode 100644 P3/main.py diff --git a/P3/Task/Propositions.py b/P3/Task/Propositions.py new file mode 100644 index 0000000..7368349 --- /dev/null +++ b/P3/Task/Propositions.py @@ -0,0 +1,284 @@ +from collections import deque +import re + +class HornKnowledgebasedAgent: + """Knowledge-based agent using Horn Clauses + Forward Chaining""" + + def __init__(self): + """Initialize the agent""" + self.knowledge_base = [] + + # ==================== MAIN INTERFACE ==================== + + def TELL(self, input_data): + """Add sensor data from gym step result or legacy string + + Args: + input_data: Either string ("S11") or gym step result tuple + """ + # Handle legacy string input + if isinstance(input_data, str): + self._add_to_kb(input_data) + self._auto_generate_rules(input_data) + return + + # Handle gym environment step result + if isinstance(input_data, tuple) and len(input_data) >= 3: + observation, reward, terminated = input_data[0], input_data[1], input_data[2] + + # Extract position (convert 0-based to 1-based) + x = observation['x'] + 1 + y = observation['y'] + 1 + position = f"{x}{y}" + + print(f"\n🎮 Processing step for position [{x},{y}] - Terminated: {terminated}") + + # Handle death vs survival + if terminated: + # Agent died - add both Wumpus AND Pit facts (Horn approximation) + self._add_to_kb(f"W{position}") + self._add_to_kb(f"P{position}") + print(f"💀 Death: W{position} ∧ P{position} (Horn approx. of W{position} ∨ P{position})") + return + else: + # Agent survived - field is safe + self._add_to_kb(f"-W{position}") + self._add_to_kb(f"-P{position}") + print(f"✅ Survival: ¬W{position} ∧ ¬P{position}") + + # Process sensors + sensors = { + 'stench': 'S', 'breeze': 'B', 'glitter': 'G', + 'bump': 'BUMP', 'scream': 'SC' + } + + for sensor_key, sensor_code in sensors.items(): + sensor_value = observation.get(sensor_key, False) + if sensor_value: + sensor_fact = f"{sensor_code}{position}" + else: + sensor_fact = f"-{sensor_code}{position}" + + print(f" Sensor: {sensor_fact}") + self._add_to_kb(sensor_fact) + self._auto_generate_rules(sensor_fact) + + def ASK(self, query): + """Check if KB entails query using Forward Chaining""" + result = self._forward_chaining(query) + print(f"ASK({query}): {result}") + return result + + def _add_to_kb(self, sentence): + """Add sentence to KB if not already present""" + if sentence not in self.knowledge_base: + self.knowledge_base.append(sentence) + print(f" Added: {sentence}") + + # ==================== FORWARD CHAINING ==================== + + def _forward_chaining(self, query): + """Forward Chaining algorithm""" + facts, rules = self._parse_kb() + + # Count premises for each rule + count = {i: len(rule['premises']) for i, rule in enumerate(rules)} + + # Track inferred symbols + inferred = {} + derived_facts = set(facts) + agenda = deque(facts) + + while agenda: + fact = agenda.popleft() + + # Check for contradiction + negated = self._negate(fact) + if negated in derived_facts: + print(f"⚠️ Contradiction: {fact} and {negated}") + return False + + # Found query? + if fact == query: + return True + + # Process rules + if not inferred.get(fact, False): + inferred[fact] = True + + for i, rule in enumerate(rules): + if fact in rule['premises']: + count[i] -= 1 + if count[i] == 0: + conclusion = rule['conclusion'] + if not inferred.get(conclusion, False): + agenda.append(conclusion) + derived_facts.add(conclusion) + + # Check if negation of query was derived + negated_query = self._negate(query) + if negated_query in derived_facts: + print(f" {query} is FALSE (KB entails {negated_query})") + return False + + print(f" {query} cannot be proven") + return False + + def _parse_kb(self): + """Parse KB into facts and rules""" + facts = [] + rules = [] + + for sentence in self.knowledge_base: + if '=>' in sentence: + premises_part, conclusion = sentence.split('=>') + premises = [p.strip() for p in premises_part.split(',')] + rules.append({'premises': premises, 'conclusion': conclusion.strip()}) + else: + facts.append(sentence) + + return facts, rules + + def _negate(self, literal): + """Negate a literal""" + return literal[1:] if literal.startswith('-') else f"-{literal}" + + # ==================== RULE GENERATION ==================== + + def _auto_generate_rules(self, sensor): + """Generate Horn rules from sensor facts""" + # STENCH rules + if re.match(r'^S(\d)(\d)$', sensor): + x, y = int(sensor[1]), int(sensor[2]) + pos = f"{x}{y}" + + # S11 => -W11 (no Wumpus in same field) + self._add_to_kb(f"S{pos}=>-W{pos}") + + # S11 => W21, S11 => W12 (direct rules for neighbors) + for nx, ny in self._get_neighbors(x, y): + self._add_to_kb(f"S{pos}=>W{nx}{ny}") + + print(f" Generated stench rules for S{pos}") + + # NO STENCH rules + elif re.match(r'^-S(\d)(\d)$', sensor): + x, y = int(sensor[2]), int(sensor[3]) + pos = f"{x}{y}" + + # -S12 => -W11, -S12 => -W22 (no Wumpus in neighbors) + for nx, ny in self._get_neighbors(x, y): + self._add_to_kb(f"-S{pos}=>-W{nx}{ny}") + + print(f" Generated no-stench rules for -S{pos}") + + # BREEZE rules + elif re.match(r'^B(\d)(\d)$', sensor): + x, y = int(sensor[1]), int(sensor[2]) + pos = f"{x}{y}" + + # B11 => -P11 (no Pit in same field) + self._add_to_kb(f"B{pos}=>-P{pos}") + + # B11 => P21, B11 => P12 (direct rules for neighbors) + for nx, ny in self._get_neighbors(x, y): + self._add_to_kb(f"B{pos}=>P{nx}{ny}") + + print(f" Generated breeze rules for B{pos}") + + # NO BREEZE rules + elif re.match(r'^-B(\d)(\d)$', sensor): + x, y = int(sensor[2]), int(sensor[3]) + pos = f"{x}{y}" + + # -B12 => -P11, -B12 => -P22 (no Pits in neighbors) + for nx, ny in self._get_neighbors(x, y): + self._add_to_kb(f"-B{pos}=>-P{nx}{ny}") + + print(f" Generated no-breeze rules for -B{pos}") + + # GLITTER rules + elif re.match(r'^G(\d)(\d)$', sensor): + x, y = int(sensor[1]), int(sensor[2]) + self._add_to_kb(f"G{x}{y}=>Gold{x}{y}") + print(f" Generated glitter rule") + + elif re.match(r'^-G(\d)(\d)$', sensor): + x, y = int(sensor[2]), int(sensor[3]) + self._add_to_kb(f"-G{x}{y}=>-Gold{x}{y}") + print(f" Generated no-glitter rule") + + # SCREAM rules + elif sensor == "SC": + self._add_to_kb("SC=>WumpusDead") + print(f" Generated scream rule") + + elif sensor == "-SC": + self._add_to_kb("-SC=>WumpusAlive") + print(f" Generated no-scream rule") + + def _get_neighbors(self, x, y): + """Get valid neighbors in 4x4 grid""" + neighbors = [] + for dx, dy in [(-1,0), (1,0), (0,-1), (0,1)]: + nx, ny = x + dx, y + dy + if 1 <= nx <= 4 and 1 <= ny <= 4: + neighbors.append((nx, ny)) + return neighbors + + def print_kb(self): + """Print knowledge base""" + print("\n=== Horn Clauses Knowledge Base ===") + facts = [s for s in self.knowledge_base if '=>' not in s] + rules = [s for s in self.knowledge_base if '=>' in s] + + print(f"Facts ({len(facts)}):") + for i, fact in enumerate(facts, 1): + print(f" {i}. {fact}") + + print(f"Rules ({len(rules)}):") + for i, rule in enumerate(rules, 1): + print(f" {i}. {rule}") + + +# ==================== TESTS ==================== + +def test_horn_gym_integration(): + """Test Horn agent with gym integration""" + print("🎯 Horn Clauses Agent - Gym Integration Test") + print("="*50) + + agent = HornKnowledgebasedAgent() + + # Agent survives at [1,1] with stench + step_result_1 = ( + {'x': 0, 'y': 0, 'stench': True, 'breeze': False, 'glitter': False, 'bump': False, 'scream': False}, + [-1], False, False, {} + ) + agent.TELL(step_result_1) + + # Agent dies at [2,1] + step_result_2 = ( + {'x': 1, 'y': 0, 'stench': False, 'breeze': False, 'glitter': False, 'bump': False, 'scream': False}, + [-1000], True, False, {} + ) + agent.TELL(step_result_2) + + print("\n🔍 Testing queries:") + agent.ASK("W11") # Should be FALSE (survived) + agent.ASK("P11") # Should be FALSE (survived) + agent.ASK("W21") # Should be TRUE (died) + agent.ASK("P21") # Should be False No breez at 11 + agent.ASK("W12") # Should be TRUE (from S11) + agent.ASK("W22") # Should be TRUE (from S11) + + agent.print_kb() + +if __name__ == "__main__": + print("🏰 Horn Clauses Agent for Wumpus World") + print("="*60) + + test_horn_gym_integration() + + print("\n✨ Horn implementation complete!") + print(" Shows clear limitations vs CNF Resolution") \ No newline at end of file diff --git a/P3/Task/Propositions2.py b/P3/Task/Propositions2.py new file mode 100644 index 0000000..05f3872 --- /dev/null +++ b/P3/Task/Propositions2.py @@ -0,0 +1,411 @@ +from collections import deque +import re + + +class KnowledgebasedAgent: + """Knowledge-based agent for Wumpus World using CNF Resolution""" + + def __init__(self, method="CNF"): + """Initialize the knowledge-based agent""" + self.method = method + self.cnf_clauses = [] # List of sets (each set is a clause) + + # ==================== MAIN INTERFACE ==================== + + def TELL(self, step_result): + """Add sensor data from gym environment step result + Args: + step_result: Can be either: + - String (legacy): "S11", "-B12", etc. + - Tuple from env.step(): (observation, reward, terminated, truncated, info) + """ + # Handle legacy string input + if isinstance(step_result, str): + # print(f"Added sensor: {step_result}") + self._generate_clauses_from_sensor(step_result) + return + + # Handle gym environment step result + if isinstance(step_result, tuple) and len(step_result) >= 3: + observation, reward, terminated = step_result[0], step_result[1], step_result[2] + + # Extract position (convert from 0-based to 1-based coordinates) + x = observation['x'] + 1 # Convert 0-based to 1-based + y = observation['y'] + 1 # Convert 0-based to 1-based + position = f"{x}{y}" + + print(f"\n Processing step result for position [{x},{y}]:") + print(f" Terminated: {terminated}, Reward: {reward}") + + # Extract sensors from observation + sensors = { + 'stench': observation.get('stench', False), + 'breeze': observation.get('breeze', False), + 'glitter': observation.get('glitter', False), + 'bump': observation.get('bump', False), + 'scream': observation.get('scream', False) + } + + # Process survival/death logic first + if terminated: + # Agent died - field contains Wumpus OR Pit + death_clause = {f"W{position}", f"P{position}"} + self.cnf_clauses.append(death_clause) + print(f" Death rule: W{position} ∨ P{position}") + + # No sensor processing when agent dies + print(f" Agent died - no sensor data processed") + return + else: + # Agent survived - field is safe (no Wumpus AND no Pit) + safe_wumpus = {f"-W{position}"} + safe_pit = {f"-P{position}"} + self.cnf_clauses.append(safe_wumpus) + self.cnf_clauses.append(safe_pit) + print(f" Survival rules: ¬W{position} ∧ ¬P{position}") + + # Process sensor readings + sensor_mapping = { + 'stench': 'S', + 'breeze': 'B', + 'glitter': 'G', + 'bump': 'BUMP', + 'scream': 'SC' + } + + for sensor_name, sensor_value in sensors.items(): + sensor_code = sensor_mapping.get(sensor_name, sensor_name.upper()) + + if sensor_value: + # Positive sensor reading + sensor_fact = f"{sensor_code}{position}" + # print(f" Sensor: {sensor_fact}") + self._add_sensor_unit_clause(sensor_fact) + self._generate_clauses_from_sensor(sensor_fact) + else: + # Negative sensor reading + sensor_fact = f"-{sensor_code}{position}" + # print(f" Sensor: {sensor_fact}") + self._add_sensor_unit_clause(sensor_fact) + self._generate_clauses_from_sensor(sensor_fact) + + return + + def _add_sensor_unit_clause(self, sensor_fact): + """Add sensor as unit clause (avoid duplicates from _generate_clauses_from_sensor)""" + unit_clause = {sensor_fact} + if unit_clause not in self.cnf_clauses: + self.cnf_clauses.append(unit_clause) + + def TELL_SENSOR(self, sensor_string): + """Legacy method for adding individual sensor strings""" + self.TELL(sensor_string) + + def TELL_STEP(self, step_result): + """Convenience method specifically for gym step results""" + self.TELL(step_result) + + def ASK(self, query): + """Check if KB entails query using CNF Resolution""" + result = self._cnf_resolution(query) + print(f"ASK({query}): {result}") + return result + + def print_clauses(self): + """Print all CNF clauses""" + print("\n=== CNF Clauses ===") + for i, clause in enumerate(self.cnf_clauses): + literals = " ∨ ".join(sorted(clause)) + print(f" {i + 1}. {{{literals}}}") + + # ==================== CNF RESOLUTION ALGORITHM ==================== + + def _cnf_resolution(self, query): + """CNF Resolution to prove KB |= query""" + # To prove KB |= query, show KB ∧ ¬query is unsatisfiable + negated_query = self._negate(query) + test_clauses = [clause.copy() for clause in self.cnf_clauses] + test_clauses.append({negated_query}) + + print(f"\nProving: KB ∧ ¬{query} is unsatisfiable") + print(f"Added negated query: {{{negated_query}}}") + + new_clauses = [] + iteration = 0 + + while True: + iteration += 1 + # print(f"\n--- Resolution Step {iteration} ---") + + # Try all pairs of clauses + for i in range(len(test_clauses)): + for j in range(i + 1, len(test_clauses)): + resolvent = self._resolve(test_clauses[i], test_clauses[j]) + + if resolvent is not None: + if len(resolvent) == 0: + # Empty clause = contradiction! + print(f"Empty clause from {test_clauses[i]} ∩ {test_clauses[j]}") + print("⚡ Contradiction found! KB entails query.") + return True + + # Check if new clause is actually new + if resolvent not in test_clauses and resolvent not in new_clauses: + new_clauses.append(resolvent) + # print(f"New: {resolvent} from {test_clauses[i]} ∩ {test_clauses[j]}") + + # If no new clauses, we're done + if not new_clauses: + print("No new clauses. Query not entailed.") + return False + + # Add new clauses + test_clauses.extend(new_clauses) + new_clauses = [] + + # Safety limit + if iteration > 20: + print("Max iterations reached.") + return False + + def _resolve(self, clause1, clause2): + """Resolve two clauses if they have complementary literals""" + for literal in clause1: + complement = self._negate(literal) + if complement in clause2: + # Found resolution: combine clauses, remove complementary pair + resolvent = (clause1 - {literal}) | (clause2 - {complement}) + return resolvent + return None + + # ==================== SENSOR RULE GENERATION ==================== + + def _generate_clauses_from_sensor(self, sensor): + """Generate CNF clauses based on sensor readings""" + # Add sensor as unit clause + self.cnf_clauses.append({sensor}) + + # Dispatch to specific sensor handlers + if self._handle_stench_sensors(sensor): return + if self._handle_breeze_sensors(sensor): return + if self._handle_glitter_sensors(sensor): return + if self._handle_scream_sensors(sensor): return + if self._handle_bump_sensors(sensor): return + + def _handle_stench_sensors(self, sensor): + """Handle stench-related sensors""" + # STENCH: S11, S22, etc. + stench_match = re.match(r'^S(\d)(\d)$', sensor) + if stench_match: + x, y = int(stench_match.group(1)), int(stench_match.group(2)) + pos = f"{x}{y}" + + # S11 → ¬W11 becomes {-S11, -W11} + self.cnf_clauses.append({f"-S{pos}", f"-W{pos}"}) + + # S11 → (W21 ∨ W12 ∨ ...) becomes {-S11, W21, W12, ...} + neighbors = self._get_neighbors(x, y) + if neighbors: + neighbor_wumpus = [f"W{nx}{ny}" for nx, ny in neighbors] + clause = {f"-S{pos}"} | set(neighbor_wumpus) + self.cnf_clauses.append(clause) + + print(f" Generated: S{pos} → ¬W{pos} ∧ (W-neighbors)") + return True + + # NO STENCH: -S12, -S22, etc. + no_stench_match = re.match(r'^-S(\d)(\d)$', sensor) + if no_stench_match: + x, y = int(no_stench_match.group(1)), int(no_stench_match.group(2)) + pos = f"{x}{y}" + + # -S12 → ¬W(neighbors) becomes {S12, -W11}, {S12, -W22}, etc. + neighbors = self._get_neighbors(x, y) + for nx, ny in neighbors: + neighbor_pos = f"{nx}{ny}" + self.cnf_clauses.append({f"S{pos}", f"-W{neighbor_pos}"}) + + print(f" Generated: ¬S{pos} → ¬W-neighbors") + return True + + return False + + def _handle_breeze_sensors(self, sensor): + """Handle breeze-related sensors""" + # BREEZE: B11, B22, etc. + breeze_match = re.match(r'^B(\d)(\d)$', sensor) + if breeze_match: + x, y = int(breeze_match.group(1)), int(breeze_match.group(2)) + pos = f"{x}{y}" + + # B11 → ¬P11 becomes {-B11, -P11} + self.cnf_clauses.append({f"-B{pos}", f"-P{pos}"}) + + # B11 → (P21 ∨ P12 ∨ ...) becomes {-B11, P21, P12, ...} + neighbors = self._get_neighbors(x, y) + if neighbors: + neighbor_pits = [f"P{nx}{ny}" for nx, ny in neighbors] + clause = {f"-B{pos}"} | set(neighbor_pits) + self.cnf_clauses.append(clause) + + print(f" Generated: B{pos} → ¬P{pos} ∧ (P-neighbors)") + return True + + # NO BREEZE: -B12, -B22, etc. + no_breeze_match = re.match(r'^-B(\d)(\d)$', sensor) + if no_breeze_match: + x, y = int(no_breeze_match.group(1)), int(no_breeze_match.group(2)) + pos = f"{x}{y}" + + # -B12 → ¬P(neighbors) becomes {B12, -P11}, {B12, -P22}, etc. + neighbors = self._get_neighbors(x, y) + for nx, ny in neighbors: + neighbor_pos = f"{nx}{ny}" + self.cnf_clauses.append({f"B{pos}", f"-P{neighbor_pos}"}) + + print(f" Generated: ¬B{pos} → ¬P-neighbors") + return True + + return False + + def _handle_glitter_sensors(self, sensor): + """Handle glitter-related sensors""" + # GLITTER: G11, G22, etc. + glitter_match = re.match(r'^G(\d)(\d)$', sensor) + if glitter_match: + x, y = int(glitter_match.group(1)), int(glitter_match.group(2)) + pos = f"{x}{y}" + + # G11 → Gold11 becomes {-G11, Gold11} + self.cnf_clauses.append({f"-G{pos}", f"Gold{pos}"}) + print(f" Generated: G{pos} → Gold{pos}") + return True + + # NO GLITTER: -G11, -G22, etc. + no_glitter_match = re.match(r'^-G(\d)(\d)$', sensor) + if no_glitter_match: + x, y = int(no_glitter_match.group(1)), int(no_glitter_match.group(2)) + pos = f"{x}{y}" + + # -G11 → ¬Gold11 becomes {G11, -Gold11} + self.cnf_clauses.append({f"G{pos}", f"-Gold{pos}"}) + print(f" Generated: ¬G{pos} → ¬Gold{pos}") + return True + + return False + + def _handle_scream_sensors(self, sensor): + """Handle scream-related sensors""" + # SCREAM: Sc + if sensor == "Sc": + # Sc → WumpusDead becomes {-Sc, WumpusDead} + self.cnf_clauses.append({"-Sc", "WumpusDead"}) + # WumpusDead → ¬W_anywhere: for all positions + for x in range(1, 5): + for y in range(1, 5): + self.cnf_clauses.append({"-WumpusDead", f"-W{x}{y}"}) + print(f" Generated: Sc → WumpusDead → ¬W(everywhere)") + return True + + # NO SCREAM: -Sc + if sensor == "-Sc": + # -Sc → WumpusAlive becomes {Sc, WumpusAlive} + self.cnf_clauses.append({"Sc", "WumpusAlive"}) + print(f" Generated: ¬Sc → WumpusAlive") + return True + + return False + + def _handle_bump_sensors(self, sensor): + """Handle bump-related sensors""" + # BUMP: Bump11, Bump22, etc. + bump_match = re.match(r'^Bump(\d)(\d)$', sensor) + if bump_match: + x, y = int(bump_match.group(1)), int(bump_match.group(2)) + print(f" Note: Bump{x}{y} indicates wall collision") + return True + + # NO BUMP: -Bump11, -Bump22, etc. + no_bump_match = re.match(r'^-Bump(\d)(\d)$', sensor) + if no_bump_match: + x, y = int(no_bump_match.group(1)), int(no_bump_match.group(2)) + print(f" Note: ¬Bump{x}{y} indicates successful movement") + return True + + return False + + # ==================== UTILITY METHODS ==================== + + def _negate(self, literal): + """Negate a literal""" + if literal.startswith('-'): + return literal[1:] + else: + return f"-{literal}" + + def _get_neighbors(self, x, y): + """Get valid neighbors in 4x4 grid""" + neighbors = [] + for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: + nx, ny = x + dx, y + dy + if 1 <= nx <= 4 and 1 <= ny <= 4: + neighbors.append((nx, ny)) + return neighbors + + +# ==================== TEST SCENARIOS ==================== + + +def test_gym_integration(): + """Test direct integration with gym environment step results""" + print("\n🎮 Test 3: Direct Gym Environment Integration") + print("="*60) + + agent = KnowledgebasedAgent("CNF") + + print("📡 Simulating gym environment step results...") + + # Simulate step result from your example + step_result_1 = ( + {'x': 0, 'y': 0, 'gold': False, 'direction': 1, 'arrow': True, + 'stench': True, 'breeze': False, 'glitter': False, 'bump': False, 'scream': False}, + [-1], # reward + False, # terminated (agent survives) + False, # truncated + {'info': 'step 1'} + ) + + # Agent survives at position [1,1] (converted from [0,0]) + agent.TELL(step_result_1) + + # Simulate death scenario + step_result_2 = ( + {'x': 1, 'y': 0, 'gold': False, 'direction': 1, 'arrow': True, + 'stench': False, 'breeze': False, 'glitter': False, 'bump': False, 'scream': False}, + [-1000], # death reward + True, # terminated (agent dies) + False, # truncated + {'info': 'death'} + ) + + # Agent dies at position [2,1] (converted from [1,0]) + agent.TELL(step_result_2) + + print("\n🔍 Testing queries with survival logic...") + agent.ASK("W11") # Should be FALSE (agent survived [1,1]) + agent.ASK("P11") # Should be FALSE (agent survived [1,1]) + agent.ASK("W21") # Could be TRUE (agent died at [2,1]) + agent.ASK("P21") # Should be False (no breez at [1,1]) + + print("\n📋 Generated clauses:") + agent.print_clauses() + + + +if __name__ == "__main__": + print("🏰 CNF Resolution for Wumpus World - Complete Implementation") + print("=" * 70) + + test_gym_integration() + + print("\n✨ CNF Resolution correctly handles all Wumpus World logic!") diff --git a/P3/Task/__pycache__/Propositions.cpython-310.pyc b/P3/Task/__pycache__/Propositions.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dff50c70a0230932cb463e99e30fc0ef6dade6f7 GIT binary patch literal 7617 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 literal 0 HcmV?d00001 diff --git a/P3/Task/__pycache__/Propositions2.cpython-310.pyc b/P3/Task/__pycache__/Propositions2.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0e8c882b49c098a099a9328add5ba7fe751c6b9 GIT binary patch literal 10181 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 G@&5vE05Vhn literal 0 HcmV?d00001 diff --git a/P3/Wumpus_Test.py b/P3/Wumpus_Test.py deleted file mode 100644 index e69de29..0000000 diff --git a/P3/ai_gym b/P3/ai_gym new file mode 160000 index 0000000..aa0a38e --- /dev/null +++ b/P3/ai_gym @@ -0,0 +1 @@ +Subproject commit aa0a38ebaf6b0ea63cf206dfa0586a82bdb9ec85 diff --git a/P3/main.py b/P3/main.py new file mode 100644 index 0000000..9cb81fd --- /dev/null +++ b/P3/main.py @@ -0,0 +1,75 @@ +import random +import gym +from ai_gym import fh_ac_ai_gym +from Task.Propositions import * +from Task.Propositions2 import * + +#Create Gym environment +wumpus_env = gym.make('Wumpus-v0', disable_env_checker=True) +wumpus_env.reset() +wumpus_env.render() + +#Define the possible actions +REVERSE_ACTION_DICT= {0: "WALK", 1:"TURNLEFT", 2:"TURNRIGHT", 3:"GRAB", 4: "SHOOT", 5:"CLIMB"} +POSSIBLE_ACTIONS = {v: k for k, v in REVERSE_ACTION_DICT.items()} + + +print("=" * 70) + +agent = KnowledgebasedAgent("CNF") +obs = wumpus_env.step(1) +print(obs) +agent.TELL(obs) +agent.ASK("W21") +agent.ASK("W12") +wumpus_env.render() + +print("=" * 70) +obs = wumpus_env.step(0) +print(obs) +agent.TELL(obs) +agent.ASK("W21") +agent.ASK("W12") +wumpus_env.render() + +print("=" * 70) +obs = wumpus_env.step(0) +print(obs) +agent.TELL(obs) +agent.ASK("P12") +agent.ASK("P23") +agent.ASK("P14") +wumpus_env.render() + +print("=" * 70) +wumpus_env.step(2) +obs = wumpus_env.step(0) +print(obs) +agent.TELL(obs) +agent.ASK("P12") +agent.ASK("P23") +agent.ASK("P14") +wumpus_env.render() + +wumpus_env.reset() +print("=" * 70) +print("=" * 70) +print("=" * 70) + + +agent = HornKnowledgebasedAgent() +wumpus_env.step(1) +obs = wumpus_env.step(2) +print(obs) +agent.TELL(obs) +agent.ASK("W21") +agent.ASK("W12") +wumpus_env.render() + +print("=" * 70) +obs = wumpus_env.step(0) +print(obs) +agent.TELL(obs) +agent.ASK("W21") +agent.ASK("W12") +wumpus_env.render()