More python files
This commit is contained in:
parent
657e0ea6a8
commit
7c63daa757
32
ExpensiveQuerySimulation.py
Normal file
32
ExpensiveQuerySimulation.py
Normal file
@ -0,0 +1,32 @@
|
||||
import LRUCache
|
||||
import time
|
||||
|
||||
|
||||
def expensive_query(query):
|
||||
print(f"Running query: {query}")
|
||||
time.sleep(2) # Simulate delay
|
||||
return f"Result for '{query}'"
|
||||
|
||||
class QueryEngine:
|
||||
def __init__(self, cache_size=5):
|
||||
self.cache = LRUCache(cache_size)
|
||||
|
||||
def run_query(self, query):
|
||||
cached = self.cache.get(query)
|
||||
if cached != -1:
|
||||
print(f"Cache hit for: {query}")
|
||||
return cached
|
||||
result = expensive_query(query)
|
||||
self.cache.put(query, result)
|
||||
return result
|
||||
|
||||
|
||||
engine = QueryEngine()
|
||||
|
||||
print(engine.run_query("SELECT * FROM users"))
|
||||
print(engine.run_query("SELECT * FROM orders"))
|
||||
print(engine.run_query("SELECT * FROM users")) # Cached!
|
||||
print(engine.run_query("SELECT * FROM products"))
|
||||
print(engine.run_query("SELECT * FROM inventory"))
|
||||
print(engine.run_query("SELECT * FROM orders")) # Might be evicted
|
||||
|
||||
@ -22,16 +22,38 @@ class Card:
|
||||
raise ValueError(f"Invalid suit: {suit}")
|
||||
self.rank = rank
|
||||
self.suit = suit
|
||||
|
||||
@staticmethod
|
||||
def safe_parse_card(card_str):
|
||||
if not isinstance(card_str, str) or len(card_str) < 2:
|
||||
return None
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
if isinstance(self.rank, int):
|
||||
return self.rank
|
||||
return 10 if self.rank in ['J', 'Q', 'K'] else 1 # Ace low
|
||||
rank = card_str[:-1]
|
||||
suit = card_str[-1]
|
||||
|
||||
if rank not in VALID_RANKS or suit not in Card.suits:
|
||||
return None
|
||||
|
||||
return Card(rank, suit)
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Card) and self.rank == other.rank and self.suit == other.suit
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.rank, self.suit))
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.rank}{self.suit}"
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
if self.rank in ['J', 'Q', 'K']:
|
||||
return 10
|
||||
elif self.rank == 'A':
|
||||
return 1
|
||||
else:
|
||||
return int(self.rank) # works for '2' to '10'
|
||||
|
||||
|
||||
class Deck:
|
||||
def __init__(self):
|
||||
@ -53,19 +75,30 @@ class DiscardPile:
|
||||
def discard(self, card):
|
||||
self.cards.append(card)
|
||||
|
||||
|
||||
class Hand:
|
||||
def __init__(self):
|
||||
self.cards = []
|
||||
|
||||
def add(self, card):
|
||||
self.cards.append(card)
|
||||
|
||||
|
||||
def remove(self, card):
|
||||
self.cards.remove(card)
|
||||
for c in self.cards:
|
||||
if c == card:
|
||||
self.cards.remove(c)
|
||||
return
|
||||
raise ValueError(f"Card {card} not found in hand.")
|
||||
|
||||
def get_all_melds(self):
|
||||
return self.find_sets() + self.find_runs()
|
||||
|
||||
def find_card(self, card_str):
|
||||
rank = card_str[:-1]
|
||||
suit = card_str[-1]
|
||||
for card in self.hand.cards:
|
||||
if card.rank == rank and card.suit == suit:
|
||||
return card
|
||||
raise ValueError(f"Card {card_str} not found in hand.")
|
||||
|
||||
def find_sets(self):
|
||||
groups = defaultdict(list)
|
||||
@ -76,10 +109,15 @@ class Hand:
|
||||
def find_runs(self):
|
||||
runs = []
|
||||
suits = defaultdict(list)
|
||||
|
||||
# Group cards by suit
|
||||
for c in self.cards:
|
||||
if not isinstance(c, Card):
|
||||
raise TypeError(f"Expected Card object, got {type(c)}: {c}")
|
||||
suits[c.suit].append(c)
|
||||
|
||||
for suit, suited_cards in suits.items():
|
||||
# For each suit, find sequential runs
|
||||
for suited_cards in suits.values():
|
||||
sorted_cards = sorted(suited_cards, key=lambda c: RANK_ORDER[str(c.rank)])
|
||||
temp = [sorted_cards[0]]
|
||||
for i in range(1, len(sorted_cards)):
|
||||
@ -93,7 +131,12 @@ class Hand:
|
||||
temp = [sorted_cards[i]]
|
||||
if len(temp) >= 3:
|
||||
runs.append(temp)
|
||||
return runs
|
||||
return runs
|
||||
|
||||
def validate_hand(self):
|
||||
for c in self.cards:
|
||||
if not isinstance(c, Card):
|
||||
raise TypeError(f"Invalid card in hand: {c} (type {type(c)})")
|
||||
|
||||
def non_overlapping_meld_combos(self, all_melds):
|
||||
valid_combos = []
|
||||
@ -114,6 +157,7 @@ class Hand:
|
||||
return valid_combos
|
||||
|
||||
def best_meld_combo(self):
|
||||
self.validate_hand()
|
||||
all_melds = self.get_all_melds()
|
||||
combos = self.non_overlapping_meld_combos(all_melds)
|
||||
best = max(combos, key=lambda combo: len(set(c for meld in combo for c in meld)), default=[])
|
||||
@ -146,11 +190,14 @@ class Player:
|
||||
return card
|
||||
|
||||
def discard(self, discard_pile, card):
|
||||
card = Card.safe_parse_card(card)
|
||||
print("Hand contains:", self.cards)
|
||||
print("Trying to remove:", card)
|
||||
self.hand.remove(card)
|
||||
discard_pile.discard(card)
|
||||
self.seen_discards.append(card)
|
||||
|
||||
def choose_discard(self, discard_pile):
|
||||
def choose_discard(self):
|
||||
hand = self.hand.cards
|
||||
best_melds = self.hand.best_meld_combo()
|
||||
used = set(c for meld in best_melds for c in meld)
|
||||
@ -173,72 +220,177 @@ def choose_discard(self, discard_pile):
|
||||
else:
|
||||
return max(hand, key=lambda c: c.value)
|
||||
|
||||
class HumanPlayer(Player):
|
||||
def show_hand(self):
|
||||
self.hand.validate_hand()
|
||||
print(f"\n🧍 {self.name}'s Hand: {', '.join(str(c) for c in self.hand.cards)}")
|
||||
print(f"Melds: {[', '.join(str(c) for c in meld) for meld in self.hand.best_meld_combo()]}")
|
||||
print(f"Deadwood: {', '.join(str(c) for c in self.hand.deadwood())} ({self.hand.deadwood_points()} pts)")
|
||||
|
||||
class AIPlayer(Player):
|
||||
def show_melds_only(self):
|
||||
print(f"\n🤖 {self.name}'s Melds:")
|
||||
for meld in self.hand.best_meld_combo():
|
||||
print(" ", ', '.join(str(c) for c in meld))
|
||||
print("Hand: [hidden]")
|
||||
|
||||
def choose_discard(self, discard_pile):
|
||||
hand = self.hand.cards
|
||||
best_melds = self.hand.best_meld_combo()
|
||||
used = set(c for meld in best_melds for c in meld)
|
||||
candidates = [c for c in hand if c not in used]
|
||||
|
||||
top = discard_pile.top()
|
||||
if top:
|
||||
# Avoid discarding same rank or suit as top card
|
||||
candidates = [c for c in candidates if c.rank != top.rank and c.suit != top.suit]
|
||||
|
||||
# Score each candidate based on risk and meld potential
|
||||
def score(card, hand):
|
||||
value_penalty = card.value
|
||||
suit_cluster = sum(1 for c in hand if c.suit == card.suit)
|
||||
rank_cluster = sum(1 for c in hand if abs(RANK_ORDER[str(c.rank)] - RANK_ORDER[str(card.rank)]) <= 2)
|
||||
return value_penalty - (suit_cluster + rank_cluster)
|
||||
|
||||
|
||||
class TextUI:
|
||||
def __init__(self, game):
|
||||
self.game = game
|
||||
|
||||
def show_player_state(self, player):
|
||||
if isinstance(player, HumanPlayer):
|
||||
print(f"\n🧍 {player.name}'s Hand: {', '.join(str(c) for c in player.hand.cards)}")
|
||||
print(f"Melds: {[', '.join(str(c) for c in meld) for meld in player.hand.best_meld_combo()]}")
|
||||
print(f"Deadwood: {', '.join(str(c) for c in player.hand.deadwood())} ({player.hand.deadwood_points()} pts)")
|
||||
elif isinstance(player, AIPlayer):
|
||||
print(f"\n🤖 {player.name}'s Melds:")
|
||||
for meld in player.hand.best_meld_combo():
|
||||
print(" ", ', '.join(str(c) for c in meld))
|
||||
print("Hand: [hidden]")
|
||||
|
||||
def show_turn_banner(self, player_name):
|
||||
print("\n" + "-" * 30)
|
||||
print(f"🎯 {player_name}'s Turn")
|
||||
print("-" * 30)
|
||||
|
||||
def show_game_over(self, winner, score):
|
||||
print("\n" + "=" * 40)
|
||||
print("🎉 GAME OVER 🎉")
|
||||
print(f"{winner} wins with {score} points!")
|
||||
print("=" * 40)
|
||||
|
||||
print("\nWould you like to play again? (Y/N)")
|
||||
choice = input("> ").strip().upper()
|
||||
if choice == "Y":
|
||||
self.reset_game()
|
||||
else:
|
||||
print("Thanks for playing Gin Rummy!")
|
||||
|
||||
def show_draw(self, player_name, card):
|
||||
print(f"{player_name} draws: {card}")
|
||||
|
||||
def show_discard(self, player_name, card):
|
||||
print(f"{player_name} discards: {card}")
|
||||
|
||||
class Game:
|
||||
def __init__(self, players):
|
||||
def __init__(self, human_player, ai_player):
|
||||
self.human = human_player
|
||||
self.ai = ai_player
|
||||
self.players = [self.human, self.ai]
|
||||
self.deck = Deck()
|
||||
self.discard_pile = DiscardPile()
|
||||
self.players = players
|
||||
self.scores = {p.name: 0 for p in players}
|
||||
self.scores = {p.name: 0 for p in self.players}
|
||||
self.ui = TextUI(self)
|
||||
|
||||
def deal(self):
|
||||
for _ in range(10):
|
||||
for player in self.players:
|
||||
self.human.hand.validate_hand()
|
||||
self.ai.hand.validate_hand()
|
||||
player.hand.add(self.deck.draw())
|
||||
self.discard_pile.discard(self.deck.draw())
|
||||
|
||||
def play_round(self):
|
||||
def play_round(self, player):
|
||||
self.deal()
|
||||
turn = 0
|
||||
while True:
|
||||
player = self.players[turn % 2]
|
||||
print(f"\n{player.name}'s turn")
|
||||
self.take_turn(player)
|
||||
current = self.players[turn % 2]
|
||||
self.ui.show_player_state(player)
|
||||
self.ui.show_turn_banner(current.name)
|
||||
|
||||
if player.hand.deadwood_points() == 0:
|
||||
print(f"{player.name} goes GIN!")
|
||||
self.score_round(player, gin=True)
|
||||
if isinstance(current, HumanPlayer):
|
||||
self.human_turn(current)
|
||||
else:
|
||||
self.ai_turn(current)
|
||||
|
||||
if current.hand.deadwood_points() == 0:
|
||||
print(f"{current.name} goes GIN!")
|
||||
self.score_round(current, gin=True)
|
||||
break
|
||||
elif player.hand.deadwood_points() <= 10:
|
||||
print(f"{player.name} knocks!")
|
||||
self.score_round(player, gin=False)
|
||||
elif current.hand.deadwood_points() <= 10:
|
||||
print(f"{current.name} knocks!")
|
||||
self.score_round(current, gin=False)
|
||||
break
|
||||
|
||||
turn += 1
|
||||
|
||||
if self.check_game_over():
|
||||
return
|
||||
|
||||
def take_turn(self, player):
|
||||
# Simple AI: always draw from stock
|
||||
def human_turn(self, player):
|
||||
choice = self.ui.prompt_draw_choice()
|
||||
from_discard = choice == "D"
|
||||
drawn = player.draw(self.deck, self.discard_pile, from_discard)
|
||||
self.ui.show_draw(player.name, drawn)
|
||||
|
||||
discard_input = input("Choose a card to discard (e.g., '5♠'): ").strip()
|
||||
try:
|
||||
discard_card = player.hand.find_card(discard_input)
|
||||
player.discard(self.discard_pile, discard_card)
|
||||
self.ui.show_discard(player.name, discard_card)
|
||||
except ValueError as e:
|
||||
print(e)
|
||||
self.human_turn(player) # retry
|
||||
|
||||
def ai_turn(self, player):
|
||||
# AI always draws from stock for now
|
||||
drawn = player.draw(self.deck, self.discard_pile, from_discard=False)
|
||||
print(f"{player.name} draws {drawn}")
|
||||
self.ui.show_draw(player.name, drawn)
|
||||
|
||||
# Discard highest deadwood card
|
||||
deadwood = player.hand.deadwood()
|
||||
if deadwood:
|
||||
discard = max(deadwood, key=lambda c: c.value)
|
||||
player.discard(self.discard_pile, discard)
|
||||
print(f"{player.name} discards {discard}")
|
||||
discard = player.choose_discard(self.discard_pile)
|
||||
player.discard(self.discard_pile, discard)
|
||||
self.ui.show_discard(player.name, discard)
|
||||
|
||||
def score_round(self, knocker, gin=False):
|
||||
opponent = [p for p in self.players if p != knocker][0]
|
||||
def score_round(knocker, opponent, gin=False):
|
||||
knocker_deadwood = knocker.hand.deadwood_points()
|
||||
opponent_deadwood = opponent.hand.deadwood_points()
|
||||
|
||||
if gin:
|
||||
score = opponent_deadwood + 25
|
||||
elif opponent_deadwood <= knocker_deadwood:
|
||||
score = (knocker_deadwood - opponent_deadwood) + 25 # undercut
|
||||
self.scores[opponent.name] += score
|
||||
print(f"{opponent.name} undercuts! Scores {score}")
|
||||
return
|
||||
else:
|
||||
score = opponent_deadwood - knocker_deadwood
|
||||
return {knocker.name: score, opponent.name: 0}, "gin"
|
||||
|
||||
self.scores[knocker.name] += score
|
||||
print(f"{knocker.name} scores {score}")
|
||||
if opponent_deadwood <= knocker_deadwood:
|
||||
score = (knocker_deadwood - opponent_deadwood) + 25
|
||||
return {knocker.name: 0, opponent.name: score}, "undercut"
|
||||
|
||||
print("\nScores:")
|
||||
for name, pts in self.scores.items():
|
||||
print(f"{name}: {pts}")
|
||||
score = opponent_deadwood - knocker_deadwood
|
||||
return {knocker.name: score, opponent.name: 0}, "knock"
|
||||
|
||||
def check_game_over(self, winning_score=100):
|
||||
for name, score in self.scores.items():
|
||||
if score >= winning_score:
|
||||
self.ui.show_game_over(winner=name, score=score)
|
||||
return True
|
||||
return False
|
||||
|
||||
def reset_game(self):
|
||||
self.deck = Deck()
|
||||
self.discard_pile = DiscardPile()
|
||||
for p in self.players:
|
||||
p.hand = Hand()
|
||||
p.seen_discards = []
|
||||
self.scores = {p.name: 0 for p in self.players}
|
||||
self.play_round()
|
||||
|
||||
# Functions to detect melds outside classes
|
||||
# Function to detect sets
|
||||
@ -256,7 +408,7 @@ def find_runs(cards):
|
||||
for c in cards:
|
||||
suits[c.suit].append(c)
|
||||
|
||||
for suit, suited_cards in suits.items():
|
||||
for suited_cards in suits.items():
|
||||
# sort by rank order
|
||||
sorted_cards = sorted(suited_cards, key=lambda c: RANK_ORDER[str(c.rank)])
|
||||
# scan for consecutive sequences
|
||||
@ -298,9 +450,12 @@ def non_overlapping_meld_combos(all_melds):
|
||||
|
||||
# start a game with two players
|
||||
if __name__ == "__main__":
|
||||
players = [Player("Alice"), Player("Bob")]
|
||||
game = Game(players)
|
||||
game.play_round()
|
||||
human = HumanPlayer("Python")
|
||||
ai = AIPlayer("Bot")
|
||||
game = Game(ai, human)
|
||||
player = human # Human starts first
|
||||
game.play_round(player)
|
||||
|
||||
# Further game logic would go here
|
||||
|
||||
|
||||
50
Graph_Database_Simulation.py
Normal file
50
Graph_Database_Simulation.py
Normal file
@ -0,0 +1,50 @@
|
||||
class GraphDB:
|
||||
def __init__(self):
|
||||
self.nodes = {} # node_id -> properties
|
||||
self.edges = {} # node_id -> set of connected node_ids
|
||||
|
||||
def add_node(self, node_id, **properties):
|
||||
self.nodes[node_id] = properties
|
||||
self.edges.setdefault(node_id, set())
|
||||
|
||||
def add_edge(self, from_node, to_node):
|
||||
if from_node in self.nodes and to_node in self.nodes:
|
||||
self.edges[from_node].add(to_node)
|
||||
self.edges[to_node].add(from_node) # undirected graph
|
||||
|
||||
def get_node(self, node_id):
|
||||
return self.nodes.get(node_id)
|
||||
|
||||
def get_neighbors(self, node_id):
|
||||
return self.edges.get(node_id, set())
|
||||
|
||||
def find_nodes_by_property(self, key, value):
|
||||
return [nid for nid, props in self.nodes.items() if props.get(key) == value]
|
||||
|
||||
def shortest_path(self, start, end):
|
||||
from collections import deque
|
||||
visited = set()
|
||||
queue = deque([(start, [start])])
|
||||
|
||||
while queue:
|
||||
current, path = queue.popleft()
|
||||
if current == end:
|
||||
return path
|
||||
visited.add(current)
|
||||
for neighbor in self.edges.get(current, []):
|
||||
if neighbor not in visited:
|
||||
queue.append((neighbor, path + [neighbor]))
|
||||
return None
|
||||
|
||||
|
||||
g = GraphDB()
|
||||
g.add_node("A", type="Person", name="Alice")
|
||||
g.add_node("B", type="Person", name="Bob")
|
||||
g.add_node("C", type="City", name="Phoenix")
|
||||
g.add_edge("A", "B")
|
||||
g.add_edge("A", "C")
|
||||
g.add_edge("B", "C")
|
||||
|
||||
print(g.get_neighbors("A")) # {'B', 'C'}
|
||||
print(g.find_nodes_by_property("type", "Person")) # ['A', 'B']
|
||||
print(g.shortest_path("B", "C")) # ['B', 'A', 'C']
|
||||
173
LRUCache.py
Normal file
173
LRUCache.py
Normal file
@ -0,0 +1,173 @@
|
||||
import json
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
||||
|
||||
class Node:
|
||||
def __init__(self, key, value):
|
||||
self.key = key
|
||||
self.value = value
|
||||
self.prev = None
|
||||
self.next = None
|
||||
|
||||
class LRUCache:
|
||||
def __init__(self, capacity):
|
||||
self.capacity = capacity
|
||||
self.cache = {} # key → Node
|
||||
self.head = None
|
||||
self.tail = None
|
||||
|
||||
def _remove(self, node):
|
||||
if node.prev:
|
||||
node.prev.next = node.next
|
||||
else:
|
||||
self.head = node.next # node was head
|
||||
if node.next:
|
||||
node.next.prev = node.prev
|
||||
else:
|
||||
self.tail = node.prev # node was tail
|
||||
|
||||
def _add_to_front(self, node):
|
||||
node.next = self.head
|
||||
node.prev = None
|
||||
if self.head:
|
||||
self.head.prev = node
|
||||
self.head = node
|
||||
if not self.tail:
|
||||
self.tail = node
|
||||
|
||||
def get(self, key):
|
||||
if key not in self.cache:
|
||||
return -1
|
||||
node = self.cache[key]
|
||||
self._remove(node)
|
||||
self._add_to_front(node)
|
||||
return node.value
|
||||
|
||||
def put(self, key, value):
|
||||
if key in self.cache:
|
||||
self._remove(self.cache[key])
|
||||
elif len(self.cache) >= self.capacity:
|
||||
del self.cache[self.tail.key]
|
||||
self._remove(self.tail)
|
||||
new_node = Node(key, value)
|
||||
self._add_to_front(new_node)
|
||||
self.cache[key] = new_node
|
||||
|
||||
def display(self):
|
||||
current = self.head
|
||||
print("Cache (MRU → LRU): ", end="")
|
||||
while current:
|
||||
print(f"[{current.key}: {current.value}]", end=" ⇄ ")
|
||||
current = current.next
|
||||
print("None")
|
||||
|
||||
def display_reverse(self):
|
||||
current = self.tail
|
||||
print("Cache (LRU → MRU): ", end="")
|
||||
while current:
|
||||
print(f"[{current.key}: {current.value}]", end=" ⇄ ")
|
||||
current = current.prev
|
||||
print("None")
|
||||
|
||||
def to_dict(self):
|
||||
result = {}
|
||||
current = self.head
|
||||
while current:
|
||||
result[current.key] = current.value
|
||||
current = current.next
|
||||
return result
|
||||
|
||||
def from_dict(self, data_dict):
|
||||
self.cache.clear()
|
||||
self.head = None
|
||||
self.tail = None
|
||||
|
||||
for key, value in data_dict.items():
|
||||
if len(self.cache) >= self.capacity:
|
||||
break # Respect capacity limit
|
||||
new_node = Node(key, value)
|
||||
self._add_to_front(new_node)
|
||||
self.cache[key] = new_node
|
||||
|
||||
|
||||
def save_to_file(self, filename=None, version="1.0"):
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||
if not filename:
|
||||
filename = f"cache_{timestamp}.json"
|
||||
|
||||
data = {
|
||||
"version": version,
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"cache": self.to_dict()
|
||||
}
|
||||
|
||||
with open(filename, 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
print(f"Cache saved to {filename}")
|
||||
|
||||
|
||||
def load_from_file(self, filename):
|
||||
try:
|
||||
with open(filename, 'r') as f:
|
||||
data = json.load(f)
|
||||
version = data.get("version", "unknown")
|
||||
timestamp = data.get("timestamp", "unknown")
|
||||
print(f"Loaded cache version {version} from {timestamp}")
|
||||
self.from_dict(data.get("cache", {}))
|
||||
except (FileNotFoundError, json.JSONDecodeError) as e:
|
||||
print(f"Failed to load cache: {e}")
|
||||
|
||||
def load_latest_cache(self, directory=".", prefix="cache_", extension=".json"):
|
||||
files = [
|
||||
f for f in os.listdir(directory)
|
||||
if f.startswith(prefix) and f.endswith(extension)
|
||||
]
|
||||
if not files:
|
||||
print("No saved cache files found.")
|
||||
return
|
||||
|
||||
latest_file = max(files) # Lexicographic sort works with timestamped names
|
||||
print(f"Loading latest cache: {latest_file}")
|
||||
self.load_from_file(os.path.join(directory, latest_file))
|
||||
|
||||
@staticmethod
|
||||
def list_saved_caches(directory=".", prefix="cache_", extension=".json"):
|
||||
files = []
|
||||
for filename in os.listdir(directory):
|
||||
if filename.startswith(prefix) and filename.endswith(extension):
|
||||
files.append(filename)
|
||||
files.sort() # Sort by name (timestamped)
|
||||
print("Saved cache files:")
|
||||
for f in files:
|
||||
print(" •", f)
|
||||
return files
|
||||
|
||||
|
||||
|
||||
|
||||
lru = LRUCache(3)
|
||||
lru.put("A", 1)
|
||||
lru.put("B", 2)
|
||||
lru.put("C", 3)
|
||||
lru.save_to_file("cache.json")
|
||||
|
||||
# Later or in another session
|
||||
new_lru = LRUCache(3)
|
||||
new_lru.load_from_file("cache.json")
|
||||
new_lru.display()
|
||||
|
||||
|
||||
lru = LRUCache(4)
|
||||
lru.put("A", 1)
|
||||
lru.put("B", 2)
|
||||
lru.put("C", 3)
|
||||
lru.put("D", 4)
|
||||
lru.save_to_file() # Automatically names file like cache_2025-09-27_09-09-00.json
|
||||
|
||||
list_saved_caches()
|
||||
|
||||
lru = LRUCache(3)
|
||||
lru.load_latest_cache()
|
||||
lru.display()
|
||||
158
Neo4j_graph_database_simulation.py
Normal file
158
Neo4j_graph_database_simulation.py
Normal file
@ -0,0 +1,158 @@
|
||||
import re
|
||||
|
||||
class Node:
|
||||
def __init__(self, node_id, labels=None, properties=None):
|
||||
self.id = node_id
|
||||
self.labels = set(labels or [])
|
||||
self.properties = properties or {}
|
||||
|
||||
class Relationship:
|
||||
def __init__(self, from_node, to_node, rel_type, properties=None):
|
||||
self.from_node = from_node
|
||||
self.to_node = to_node
|
||||
self.type = rel_type
|
||||
self.properties = properties or {}
|
||||
|
||||
class GraphDB:
|
||||
def __init__(self):
|
||||
self.nodes = {} # node_id -> Node
|
||||
self.relationships = [] # list of Relationship objects
|
||||
|
||||
def add_node(self, node_id, labels=None, **properties):
|
||||
self.nodes[node_id] = Node(node_id, labels, properties)
|
||||
|
||||
def add_relationship(self, from_node, to_node, rel_type, **properties):
|
||||
if from_node in self.nodes and to_node in self.nodes:
|
||||
self.relationships.append(Relationship(from_node, to_node, rel_type, properties))
|
||||
|
||||
def match(self, label=None, property_key=None, property_value=None):
|
||||
return [
|
||||
node for node in self.nodes.values()
|
||||
if (not label or label in node.labels) and
|
||||
(not property_key or node.properties.get(property_key) == property_value)
|
||||
]
|
||||
|
||||
def match_relationship(self, rel_type=None, from_node=None, to_node=None):
|
||||
return [
|
||||
rel for rel in self.relationships
|
||||
if (not rel_type or rel.type == rel_type) and
|
||||
(not from_node or rel.from_node == from_node) and
|
||||
(not to_node or rel.to_node == to_node)
|
||||
]
|
||||
|
||||
def parse_properties(prop_str):
|
||||
props = {}
|
||||
for pair in re.findall(r"(\w+):\s*('([^']*)'|\d+|true|false)", prop_str):
|
||||
key, raw_val, str_val = pair
|
||||
if raw_val.startswith("'"):
|
||||
props[key] = str_val
|
||||
elif raw_val in ['true', 'false']:
|
||||
props[key] = raw_val == 'true'
|
||||
else:
|
||||
props[key] = int(raw_val)
|
||||
return props
|
||||
|
||||
def parse_where_clause(where_str):
|
||||
comparisons = re.findall(r"(\w+)\.(\w+)\s*(=|>|<|>=|<=)\s*('([^']*)'|\d+|true|false)", where_str)
|
||||
filters = []
|
||||
for var, key, op, raw_val, str_val in comparisons:
|
||||
if raw_val.startswith("'"):
|
||||
val = str_val
|
||||
elif raw_val in ['true', 'false']:
|
||||
val = raw_val == 'true'
|
||||
else:
|
||||
val = int(raw_val)
|
||||
filters.append((key, op, val))
|
||||
return filters
|
||||
|
||||
|
||||
class CypherEngine:
|
||||
def __init__(self, graph):
|
||||
self.graph = graph
|
||||
|
||||
def run(self, query):
|
||||
# Basic MATCH node pattern: MATCH (n:Label {key: value}) RETURN n
|
||||
match_node = re.match(r"MATCH\s+\((\w+):(\w+)\s*\{(\w+):\s*'([^']+)'\}\)\s+RETURN\s+\1", query)
|
||||
if match_node:
|
||||
var, label, key, value = match_node.groups()
|
||||
return self.graph.match(label=label, property_key=key, property_value=value)
|
||||
|
||||
# (e.g., MATCH (n:Person {name: 'Alice', age: 30}) RETURN n)
|
||||
match_node = re.match(r"MATCH\s+\((\w+):(\w+)\s*\{([^}]+)\}\)\s+RETURN\s+\1", query)
|
||||
if match_node:
|
||||
var, label, prop_str = match_node.groups()
|
||||
props = GraphDB.parse_properties(prop_str)
|
||||
return self.graph.match(label=label, **props)
|
||||
|
||||
# (e.g., MATCH (n:Person) WHERE n.age > 25 RETURN n)
|
||||
match_node = re.match(r"MATCH\s+\((\w+):(\w+)\)\s*(?:WHERE\s+(.+?))?\s+RETURN\s+\1", query)
|
||||
if match_node:
|
||||
var, label, where_str = match_node.groups()
|
||||
nodes = self.graph.match(label=label)
|
||||
if where_str:
|
||||
filters = GraphDB.parse_where_clause(where_str)
|
||||
def passes(node):
|
||||
for key, op, val in filters:
|
||||
node_val = node.properties.get(key)
|
||||
if node_val is None:
|
||||
return False
|
||||
if op == '=' and node_val != val:
|
||||
return False
|
||||
if op == '>' and not node_val > val:
|
||||
return False
|
||||
if op == '<' and not node_val < val:
|
||||
return False
|
||||
if op == '>=' and not node_val >= val:
|
||||
return False
|
||||
if op == '<=' and not node_val <= val:
|
||||
return False
|
||||
return True
|
||||
nodes = [n for n in nodes if passes(n)]
|
||||
return nodes
|
||||
|
||||
|
||||
# Basic MATCH relationship pattern: MATCH (a)-[:TYPE]->(b) RETURN a,b
|
||||
match_rel = re.match(r"MATCH\s+\((\w+)\)-\[:(\w+)\]->\((\w+)\)\s+RETURN\s+\1,\s*\3", query)
|
||||
# MATCH (a:Person)-[:FRIEND]->(b:Person) RETURN a,b
|
||||
match_rel = re.match(r"MATCH\s+\((\w+):(\w+)?\)-\[:(\w+)\]->\((\w+):(\w+)?\)\s+RETURN\s+\1,\s*\4", query)
|
||||
if match_rel:
|
||||
from_var, from_label, rel_type, to_var, to_label = match_rel.groups()
|
||||
rels = self.graph.match_relationship(rel_type=rel_type)
|
||||
return [
|
||||
rel for rel in rels
|
||||
if (not from_label or from_label in self.graph.nodes[rel.from_node].labels) and
|
||||
(not to_label or to_label in self.graph.nodes[rel.to_node].labels)
|
||||
]
|
||||
return "Unsupported query format"
|
||||
|
||||
# Example usage and test cases
|
||||
g = GraphDB()
|
||||
g.add_node("A", labels=["Person"], name="Alice", age=30)
|
||||
g.add_node("B", labels=["Person"], name="Bob", age=25)
|
||||
g.add_node("C", labels=["City"], name="Phoenix")
|
||||
|
||||
g.add_relationship("A", "B", "FRIEND", since=2020)
|
||||
g.add_relationship("A", "C", "LIVES_IN")
|
||||
|
||||
# Match nodes with label 'Person'
|
||||
people = g.match(label="Person")
|
||||
# Match relationships of type 'FRIEND'
|
||||
friendships = g.match_relationship(rel_type="FRIEND")
|
||||
engine = CypherEngine(g)
|
||||
result1 = engine.run("MATCH (n:Person {name: 'Alice'}) RETURN n")
|
||||
result2 = engine.run("MATCH (n:Person) WHERE n.age > 25 RETURN n")
|
||||
result3 = engine.run("MATCH (a:Person)-[:FRIEND]->(b:Person) RETURN a,b")
|
||||
print("People:", [(p.id, p.properties) for p in people])
|
||||
print("Friendships:", [ (rel.from_node, rel.to_node, rel.properties) for rel in friendships])
|
||||
print("Result 1:", [(n.id, n.properties) for n in result1])
|
||||
print("Result 2:", [(n.id, n.properties) for n in result2])
|
||||
print("Result 3:", [ (rel.from_node, rel.to_node, rel.properties) for rel in result3])
|
||||
|
||||
# Expected Output:
|
||||
# People: [('A', {'name': 'Alice', 'age': 30}), ('B', {'name': 'Bob', 'age': 25})]
|
||||
# Friendships: [('A', 'B', {'since': 2020})]
|
||||
# Result 1: [('A', {'name': 'Alice', 'age': 30})]
|
||||
# Result 2: [('A', {'name': 'Alice', 'age': 30))]
|
||||
# Result 3: [('A', 'B', {'since': 2020})]
|
||||
|
||||
|
||||
118
linked_list.py
Normal file
118
linked_list.py
Normal file
@ -0,0 +1,118 @@
|
||||
class Node:
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.prev = None
|
||||
self.next = None
|
||||
|
||||
class DoublyLinkedList:
|
||||
def __init__(self):
|
||||
self.head = None
|
||||
|
||||
def append(self, data):
|
||||
new_node = Node(data)
|
||||
if not self.head:
|
||||
self.head = new_node
|
||||
return
|
||||
current = self.head
|
||||
while current.next:
|
||||
current = current.next
|
||||
current.next = new_node
|
||||
new_node.prev = current
|
||||
|
||||
def insert(self, index, data):
|
||||
new_node = Node(data)
|
||||
if index == 0:
|
||||
new_node.next = self.head
|
||||
if self.head:
|
||||
self.head.prev = new_node
|
||||
self.head = new_node
|
||||
return
|
||||
current = self.head
|
||||
for _ in range(index - 1):
|
||||
if not current:
|
||||
raise IndexError("Index out of bounds")
|
||||
current = current.next
|
||||
if not current:
|
||||
raise IndexError("Index out of bounds")
|
||||
new_node.next = current.next
|
||||
new_node.prev = current
|
||||
if current.next:
|
||||
current.next.prev = new_node
|
||||
current.next = new_node
|
||||
|
||||
def delete(self, data):
|
||||
current = self.head
|
||||
while current:
|
||||
if current.data == data:
|
||||
if current.prev:
|
||||
current.prev.next = current.next
|
||||
else:
|
||||
self.head = current.next
|
||||
if current.next:
|
||||
current.next.prev = current.prev
|
||||
return
|
||||
current = current.next
|
||||
raise ValueError(f"{data} not found in list")
|
||||
|
||||
def reverse(self):
|
||||
current = self.head
|
||||
prev_node = None
|
||||
while current:
|
||||
next_node = current.next
|
||||
current.next = prev_node
|
||||
current.prev = next_node
|
||||
prev_node = current
|
||||
current = next_node
|
||||
self.head = prev_node
|
||||
|
||||
def find(self, data):
|
||||
current = self.head
|
||||
index = 0
|
||||
while current:
|
||||
if current.data == data:
|
||||
return index
|
||||
current = current.next
|
||||
index += 1
|
||||
return -1
|
||||
|
||||
def to_list(self):
|
||||
result = []
|
||||
current = self.head
|
||||
while current:
|
||||
result.append(current.data)
|
||||
current = current.next
|
||||
return result
|
||||
|
||||
def display_forward(self):
|
||||
current = self.head
|
||||
while current:
|
||||
print(current.data, end=" ⇄ ")
|
||||
current = current.next
|
||||
print("None")
|
||||
|
||||
def display_backward(self):
|
||||
current = self.head
|
||||
if not current:
|
||||
print("None")
|
||||
return
|
||||
while current.next:
|
||||
current = current.next
|
||||
while current:
|
||||
print(current.data, end=" ⇄ ")
|
||||
current = current.prev
|
||||
print("None")
|
||||
|
||||
dll = DoublyLinkedList()
|
||||
dll.append(10)
|
||||
dll.append(20)
|
||||
dll.append(30)
|
||||
dll.insert(1, 15) # Insert 15 at index 1
|
||||
dll.delete(20) # Remove node with value 20
|
||||
dll.reverse() # Reverse the list
|
||||
print("Index of 15:", dll.find(15))
|
||||
dll.append(50)
|
||||
print(dll.to_list()) # Output: [30, 15, 10, 50]
|
||||
|
||||
|
||||
dll.display_forward() # Output: 30 ⇄ 15 ⇄ 10 ⇄ None
|
||||
dll.display_backward() # Output: 10 ⇄ 15 ⇄ 30 ⇄ None
|
||||
Loading…
Reference in New Issue
Block a user