#!/usr/bin/env python3 # Copyright (c) Napol Thanarangkaun # Licensed under the MIT License - See LICENSE file for details from array import array import argparse import base64 import copy import json import math import os import secrets import struct import time import urllib.error import urllib.parse import urllib.request CONFIG = { "VERSION": 2, "MAX_FILE_SIZE": 1024 * 1024, "TARGET_FILE_SIZE": 950 * 1024, "INPUT_SIZE": 16, "HIDDEN_SIZE": 32, "OUTPUT_SIZE": 8, "MEMORY_LIMIT": 200, "RULE_LIMIT": 64, "BEHAVIOR_LIMIT": 128, "LEARNING_RATE": 0.02, "MIN_WEIGHT": -5, "MAX_WEIGHT": 5, "STATE_MIN": 0, "STATE_MAX": 100, } MAGIC = b"DODL" HEADER_SIZE = 16 PIXEL_INPUT_SIZE = 8 PIXEL_OUTPUT_SIZE = 2 def clamp(value, minimum, maximum): return max(minimum, min(maximum, value)) def random_float(minimum, maximum): return minimum + secrets.randbelow(1 << 53) / (1 << 53) * (maximum - minimum) def random_int(minimum, maximum): return secrets.randbelow(maximum - minimum + 1) + minimum def uuid_bytes(): return secrets.token_bytes(16).hex() def now(): return int(time.time() * 1000) class Random: def __init__(self, seed=None): if seed is None: seed = now() self.seed = int(seed) & 0xFFFFFFFF or 1 def next(self): value = self.seed value ^= (value << 13) & 0xFFFFFFFF value ^= value >> 17 value ^= (value << 5) & 0xFFFFFFFF self.seed = value & 0xFFFFFFFF return self.seed / 0x100000000 def float(self, minimum, maximum): return minimum + self.next() * (maximum - minimum) def encode_model_for_storage(buffer): return base64.b64encode(buffer).decode("ascii") def decode_model_from_storage(encoded): return base64.b64decode(encoded, validate=True) def create_initial_state(): timestamp = now() return { "energy": 72, "curiosity": 50, "confidence": 50, "stability": 80, "cycle": 0, "lastAction": None, "lastReward": 0, "createdAt": timestamp, "updatedAt": timestamp, } def create_initial_goals(): return [ {"id": "survive", "priority": 100, "target": {"energyMin": 20}}, {"id": "explore", "priority": 50, "target": {"curiosityMin": 60}}, {"id": "learn", "priority": 40, "target": {"confidenceMin": 50}}, ] def create_initial_rules(): return [ { "id": "rest-low-energy", "condition": {"type": "less-than", "variable": "energy", "value": 20}, "action": {"type": "rest"}, "priority": 100, "enabled": True, "success": 0, "failure": 0, }, { "id": "explore-high-curiosity", "condition": {"type": "greater-than", "variable": "curiosity", "value": 70}, "action": {"type": "explore"}, "priority": 60, "enabled": True, "success": 0, "failure": 0, }, { "id": "observe-default", "condition": {"type": "always"}, "action": {"type": "observe"}, "priority": 10, "enabled": True, "success": 0, "failure": 0, }, ] def create_initial_behavior(): return [ ["READ", "energy"], ["LESS_THAN", 20], ["IF"], ["ACTION", "rest"], ["ELSE"], ["READ", "curiosity"], ["GREATER_THAN", 70], ["IF"], ["ACTION", "explore"], ["ELSE"], ["ACTION", "observe"], ["END"], ["END"], ] def _float32(value): return struct.unpack("