Genetic Turmit Evolver
python
c
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

turmit.py 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. import logging
  2. from .rpnlib import *
  3. logger = logging.getLogger()
  4. class Turmit(object):
  5. ''' @brief Represent a turmit that act given an RPN expression with an
  6. infinite looping stack with variable stack size.
  7. '''
  8. def __init__(self, stack_size=8, **kwargs):
  9. ''' @brief Instanciated a new Turmit with a programm and a memory stack
  10. @param stack_size int : stack_size
  11. @param prog str | RpnExpr | None : turmit programm if None generate
  12. it randomlt
  13. @param prog_size int : if prog is generated randomly it will make
  14. this size
  15. @param max_int int : maximum integer value + 1
  16. @param signed_int bool : if True integers are signed
  17. @throw ValueError for bad option value
  18. @throw RuntimError for bad arguments
  19. '''
  20. if stack_size < 2:
  21. msg = 'Stack size has to be >= 2 but %d given' % (stack_size)
  22. raise ValueError(msg)
  23. ## @brief List that represent a stack
  24. self._stack = [ 0 for _ in range(stack_size)]
  25. ## @brief Stack head index
  26. self._cur = stack_size - 1
  27. ## @brief Stores turmit direction
  28. self._dir = 0
  29. ## @brief Stores turmit program
  30. self._prog = None
  31. self._prog_sz = 42
  32. ## @brief Stores maximum int value + 1
  33. # @note This limit is handled by @ref rpnlib.RpnOp()
  34. self._max_int = 0x10000
  35. ## @brief If True integers are signed
  36. # @note This limit is handled by @ref rpnlib.RpnOp()
  37. self._signed_int = True
  38. if 'max_int' in kwargs:
  39. v = kwargs['max_int']
  40. if v < 1:
  41. raise ValueError('Maximum int have to be >= 1 but %d found' % v)
  42. self._max_int = kwargs['max_int']
  43. del(kwargs['max_int'])
  44. if 'signed_int' in kwargs:
  45. self._signed_int = bool(kwargs['signed_int'])
  46. del(kwargs['signed_int'])
  47. if 'prog_size' in kwargs:
  48. v = kwargs['prog_size']
  49. if v < 2:
  50. raise ValueError('Prog_size have to be >= 2 but %d found' % v)
  51. self._prog_sz = v
  52. del(kwargs['prog_size'])
  53. if 'prog' in kwargs:
  54. v = kwargs['prog']
  55. if isinstance(v, str):
  56. v = RpnExpr.from_string(v)
  57. if isinstance(v, RpnExpr):
  58. self._prog = v
  59. else:
  60. msg = 'Str or RpnExpr expected but %s found' % (type(v))
  61. raise TypeError(msg)
  62. del(kwargs['prog'])
  63. if len(kwargs) > 0:
  64. msg = 'Unexpected arguments : [%s]' % ', '.join(kwargs.keys())
  65. raise RuntimeError(msg)
  66. if self._prog is None:
  67. self._prog = RpnExpr.random(self._prog_sz)
  68. self._determin_score = 5
  69. self.__expr_fun = lambda *args, **kwargs: (0, True, None)
  70. def __call__(self, **context):
  71. ''' @brief Exec the RPN expression and return the stack head
  72. @param context dict : dict with variable values (see @ref rpnlib._var_list)
  73. @return stack head after RPN
  74. '''
  75. context = {k:IntVar(context[k]) % self._max_int for k in context}
  76. if self.__expr_fun is None:
  77. self._compile()
  78. ret = self.__expr_fun(self, **context)
  79. determinist = ret[1]
  80. if len(ret) == 2:
  81. ret = ret[0]
  82. return ret
  83. #if len(ret) == 3 -> returned a continuation address in RPN expr
  84. #self.__ip = 0
  85. self.__ip = ret[0]
  86. while self.__ip < len(self._prog):
  87. sym = self._prog[self.__ip]
  88. if sym.optype == RpnSymbol.VALUE:
  89. self._push(sym.value)
  90. elif sym.optype == RpnSymbol.VARIABLE:
  91. self._push(IntVar(context[sym.value.lower()]))
  92. elif sym.optype == RpnSymbol.OPERATION:
  93. op = getattr(self, sym.value.lower())
  94. if determinist:
  95. if not op():
  96. determinist = False
  97. else:
  98. op()
  99. self.__ip += 1
  100. self._determin_score += 0 if determinist else 1
  101. return self.shead
  102. def _compile(self):
  103. code = '''def expr_fun(self, x, y, r, g, b):
  104. determinist = True'''
  105. op2_head = '''a = self._stack[self._cur]
  106. self._cur = (self._cur - 1) if self._cur > 0 else (len(self._stack) - 1)
  107. if isinstance(a, IntVar) or isinstance(self._stack[self._cur], IntVar):
  108. determinist = False
  109. '''
  110. for ip, sym in enumerate(self._prog):
  111. if sym.optype == RpnSymbol.VALUE:
  112. code += '''
  113. self._cur += 1
  114. self._cur %%= len(self._stack)
  115. self._stack[self._cur] = %d
  116. ''' % sym.value
  117. elif sym.optype == RpnSymbol.VARIABLE:
  118. code += '''
  119. self._cur += 1
  120. self._cur %%= len(self._stack)
  121. self._stack[self._cur] = %s
  122. ''' % sym.value.lower()
  123. elif sym.value == 'add':
  124. code += '''
  125. %s
  126. self._stack[self._cur] += a
  127. self._stack[self._cur] %= self._max_int
  128. ''' % op2_head
  129. elif sym.value == 'sub':
  130. code += '''
  131. %s
  132. self._stack[self._cur] = self._stack[self._cur] - a
  133. if self._stack[self._cur] < 0:
  134. self._stack[self._cur] += self._max_int
  135. ''' % op2_head
  136. elif sym.value == 'mul':
  137. code += '''
  138. %s
  139. self._stack[self._cur] *= a
  140. self._stack[self._cur] %= self._max_int
  141. ''' % op2_head
  142. elif sym.value == 'div':
  143. code += '''
  144. %s
  145. if a != 0:
  146. self._stack[self._cur] = self._stack[self._cur] // a
  147. ''' % op2_head
  148. elif sym.value == 'mod':
  149. code += '''
  150. %s
  151. if a != 0:
  152. self._stack[self._cur] = self._stack[self._cur] % a
  153. ''' % op2_head
  154. elif sym.value == 'bin_and':
  155. code += '''
  156. %s
  157. self._stack[self._cur] &= a
  158. ''' % op2_head
  159. elif sym.value == 'bin_or':
  160. code += '''
  161. %s
  162. self._stack[self._cur] |= a
  163. ''' % op2_head
  164. elif sym.value == 'bin_xor':
  165. code += '''
  166. %s
  167. self._stack[self._cur] ^= a
  168. ''' % op2_head
  169. elif sym.value == 'lshift':
  170. code += '''
  171. %s
  172. self._stack[self._cur] = (self._stack[self._cur] << a) % self._max_int
  173. ''' % op2_head
  174. elif sym.value == 'rshift':
  175. code += '''
  176. %s
  177. self._stack[self._cur] = self._stack[self._cur] >> a
  178. ''' % op2_head
  179. elif sym.value == 'dup':
  180. code += '''
  181. a = self._stack[self._cur]
  182. if isinstance(a, IntVar):
  183. determinist = False
  184. self._cur += 1
  185. self._cur %= len(self._stack)
  186. self._stack[self._cur] = a
  187. '''
  188. elif sym.value == 'pop':
  189. code += '''
  190. self._cur = (self._cur - 1) if self._cur > 0 else (len(self._stack) - 1)
  191. '''
  192. else:
  193. code += '''
  194. return (%d,determinist, None)
  195. ''' % ip
  196. code += '''
  197. return (self._stack[self._cur], determinist)
  198. '''
  199. g = {}
  200. l = {}
  201. exec(code, g, l)
  202. self.__expr_fun = l['expr_fun']
  203. @property
  204. def shead(self):
  205. ''' @brief Stack head attribute '''
  206. return self._stack[self._cur]
  207. def _pop(self):
  208. ''' @brief Pops a value from the stack
  209. @note If stack head is 0, set the stack head to len(self._stack)
  210. - 1
  211. @return popped value
  212. '''
  213. res = self._stack[self._cur]
  214. if self._cur <= 0:
  215. self._cur = len(self._stack) - 1
  216. else:
  217. self._cur -= 1
  218. return res
  219. def _push(self, val):
  220. ''' @brief Pushes a value on the stack
  221. @note If no space left on the stack, the head is set to 0
  222. @param val int : value to push
  223. '''
  224. self._cur += 1
  225. if self._cur >= len(self._stack) or abs(self._cur) >= len(self._stack):
  226. self._cur = 0
  227. self._stack[self._cur] = self._cast_int(val)
  228. def _cast_int(self, val):
  229. ''' @brief Transform integer given Turmit max int and signed_int
  230. attr
  231. @param val int : integer to transform
  232. @return integer
  233. '''
  234. intvar = isinstance(val, IntVar)
  235. val = int(val)
  236. if self._max_int is not None:
  237. val %= self._max_int
  238. if not self._signed_int and val < 0:
  239. val = self._max_int - val
  240. return IntVar(val) if intvar else val
  241. @RpnOp
  242. def mem_sz(self, new_sz):
  243. ''' @brief Update memory stack size
  244. '''
  245. stksz = len(self._stack)
  246. new_sz %= 0xFFF
  247. if new_sz < 2:
  248. new_sz = 2
  249. if new_sz > stksz:
  250. self._stack += [ 0 for _ in range(new_sz - stksz) ]
  251. elif new_sz < stksz:
  252. self._stack = self._stack[0:new_sz]
  253. if self._cur >= new_sz:
  254. self._cur = 0
  255. @RpnOp
  256. def add(self, a, b):
  257. ''' @brief Adds one value to another
  258. @param a int : value
  259. @param b int : value to add to a
  260. @return a + b
  261. '''
  262. return a + b
  263. @RpnOp
  264. def sub(self, a, b):
  265. ''' @brief Substitutes a value to another
  266. @param a int : value
  267. @param b int : value to substitute to a
  268. @return a - b
  269. '''
  270. return a - b
  271. @RpnOp
  272. def bin_and(self, a, b):
  273. ''' @brief Binary and
  274. @param a int : value
  275. @param b int : value
  276. @return a & b
  277. '''
  278. return a & b
  279. @RpnOpNoMod
  280. def dup(self, a):
  281. ''' @brief Duplicates stack head
  282. @param a int : value
  283. @return a
  284. '''
  285. self._push(a)
  286. return a
  287. @RpnOp
  288. def lshift(self, a, b):
  289. ''' @brief Left shifts a of b
  290. @param a int : value
  291. @param b int : value
  292. @return a << b
  293. '''
  294. return a << b
  295. @RpnOp
  296. def mod(self, a, b):
  297. ''' @brief Gives a modulo b
  298. @param a int : value
  299. @param b int : value
  300. @return a % b
  301. '''
  302. return a % b
  303. @RpnOp
  304. def mul(self, a, b):
  305. ''' @brief Multiplies a with b
  306. @param a int : value
  307. @param b int : value
  308. @return a * b
  309. '''
  310. return a * b
  311. @RpnOp
  312. def div(self, a, b):
  313. ''' @brief Divides a with b
  314. @param a int : value
  315. @param b int : value
  316. @return a // b
  317. '''
  318. return a // b
  319. @RpnOp
  320. def bin_or(self, a, b):
  321. ''' @brief Binary or
  322. @param a int : value
  323. @param b int : value
  324. @return a | b
  325. '''
  326. return a | b
  327. @RpnOp
  328. def bin_xor(self, a, b):
  329. ''' @brief Binary xor
  330. @param a int : value
  331. @param b int : value
  332. @return a ^ b
  333. '''
  334. return a ^ b
  335. @RpnOpNoMod
  336. def pop(self, a):
  337. ''' @brief Pops a, head of the stack
  338. @param a int : value
  339. '''
  340. pass
  341. @RpnOpNoMod
  342. def swp(self, a, b):
  343. ''' @brief Swap the two values on top of the stack
  344. @param a int : v1
  345. @param b int : v2
  346. '''
  347. self._push(b)
  348. self._push(a)
  349. @RpnOp
  350. def jmp(self, offset):
  351. ''' @brief Increments IP
  352. @param offset int : jump offset symbols
  353. '''
  354. self.__ip += offset
  355. @RpnOp
  356. def jz(self, a, offset):
  357. ''' @brief Increments IP if a == 0
  358. @param a int : operand
  359. @param offset int : jump offset symbols
  360. '''
  361. if a == 0:
  362. self.__ip += offset
  363. """
  364. @RpnOp
  365. def jnz(self, a, offset):
  366. ''' @brief Increments IP if a != 0
  367. @param a int : operand
  368. @param offset int : jump offset symbols
  369. '''
  370. if a != 0:
  371. self.__ip += offset
  372. """
  373. @RpnOp
  374. def jcmp(self, a, b, cnd, offset):
  375. ''' @brief Increments IP if a == b
  376. Condition type is encoded on an int :
  377. - 0b0000 : je
  378. - 0b0001 : jne
  379. - 0b0010 : jle
  380. - 0b0011 : jl
  381. - 0b0100 : jg
  382. - 0b0101 : jge
  383. - 0b0110 : jne
  384. - 0b0111 : jmp
  385. @param a int : operand
  386. @param b int : operand
  387. @param cnd int : condition type
  388. @param offset int : jump offset symbols
  389. '''
  390. jmp = False
  391. if cnd & 1 == 0 and a == b or cnd & 1 and a != b or\
  392. cnd & 0b10 and a < b or cnd & 0b100 and a > b:
  393. self.__ip += offset