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 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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.__expr_fun = lambda *args, **kwargs: (0,)
  69. def __call__(self, **context):
  70. ''' @brief Exec the RPN expression and return the stack head
  71. @param context dict : dict with variable values (see @ref rpnlib._var_list)
  72. @return stack head after RPN
  73. '''
  74. context = {k:int(context[k]) % self._max_int for k in context}
  75. if self.__expr_fun is None:
  76. self._compile()
  77. ret = self.__expr_fun(self, **context)
  78. if isinstance(ret, int):
  79. return ret
  80. #self.__ip = 0
  81. self.__ip = ret[0]
  82. while self.__ip < len(self._prog):
  83. sym = self._prog[self.__ip]
  84. if sym.optype == RpnSymbol.VALUE:
  85. self._push(sym.value)
  86. elif sym.optype == RpnSymbol.VARIABLE:
  87. self._push(context[sym.value.lower()])
  88. elif sym.optype == RpnSymbol.OPERATION:
  89. getattr(self, sym.value.lower())()
  90. self.__ip += 1
  91. return self.shead
  92. def _compile(self):
  93. code = 'def expr_fun(self, x, y, r, g, b):\n'
  94. for ip, sym in enumerate(self._prog):
  95. if sym.optype == RpnSymbol.VALUE:
  96. code += '''
  97. self._cur += 1
  98. self._cur %%= len(self._stack)
  99. self._stack[self._cur] = %d
  100. ''' % sym.value
  101. elif sym.optype == RpnSymbol.VARIABLE:
  102. code += '''
  103. self._cur += 1
  104. self._cur %%= len(self._stack)
  105. self._stack[self._cur] = %s
  106. ''' % sym.value.lower()
  107. elif sym.value == 'add':
  108. code += '''
  109. a = self._stack[self._cur]
  110. self._cur = (self._cur - 1) if self._cur > 0 else (len(self._stack) - 1)
  111. self._stack[self._cur] += a
  112. self._stack[self._cur] %= self._max_int
  113. '''
  114. elif sym.value == 'sub':
  115. code += '''
  116. a = self._stack[self._cur]
  117. self._cur = (self._cur - 1) if self._cur > 0 else (len(self._stack) - 1)
  118. self._stack[self._cur] = self._stack[self._cur] - a
  119. if self._stack[self._cur] < 0:
  120. self._stack[self._cur] += self._max_int
  121. '''
  122. elif sym.value == 'mul':
  123. code += '''
  124. a = self._stack[self._cur]
  125. self._cur = (self._cur - 1) if self._cur > 0 else (len(self._stack) - 1)
  126. self._stack[self._cur] *= a
  127. self._stack[self._cur] %= self._max_int
  128. '''
  129. elif sym.value == 'div':
  130. code += '''
  131. a = self._stack[self._cur]
  132. self._cur = (self._cur - 1) if self._cur > 0 else (len(self._stack) - 1)
  133. if a != 0:
  134. self._stack[self._cur] = self._stack[self._cur] // a
  135. '''
  136. elif sym.value == 'mod':
  137. code += '''
  138. a = self._stack[self._cur]
  139. self._cur = (self._cur - 1) if self._cur > 0 else (len(self._stack) - 1)
  140. if a != 0:
  141. self._stack[self._cur] = self._stack[self._cur] % a
  142. '''
  143. elif sym.value == 'bin_and':
  144. code += '''
  145. a = self._stack[self._cur]
  146. self._cur = (self._cur - 1) if self._cur > 0 else (len(self._stack) - 1)
  147. self._stack[self._cur] &= a
  148. '''
  149. elif sym.value == 'bin_or':
  150. code += '''
  151. a = self._stack[self._cur]
  152. self._cur = (self._cur - 1) if self._cur > 0 else (len(self._stack) - 1)
  153. self._stack[self._cur] |= a
  154. '''
  155. elif sym.value == 'bin_xor':
  156. code += '''
  157. a = self._stack[self._cur]
  158. self._cur = (self._cur - 1) if self._cur > 0 else (len(self._stack) - 1)
  159. self._stack[self._cur] ^= a
  160. '''
  161. elif sym.value == 'lshift':
  162. code += '''
  163. a = self._stack[self._cur]
  164. self._cur = (self._cur - 1) if self._cur > 0 else (len(self._stack) - 1)
  165. self._stack[self._cur] = (self._stack[self._cur] << a) % self._max_int
  166. '''
  167. elif sym.value == 'rshift':
  168. code += '''
  169. a = self._stack[self._cur]
  170. self._cur = (self._cur - 1) if self._cur > 0 else (len(self._stack) - 1)
  171. self._stack[self._cur] = self._stack[self._cur] >> a
  172. '''
  173. elif sym.value == 'dup':
  174. code += '''
  175. a = self._stack[self._cur]
  176. self._cur += 1
  177. self._cur %= len(self._stack)
  178. self._stack[self._cur] = a
  179. '''
  180. elif sym.value == 'pop':
  181. code += '''
  182. self._cur = (self._cur - 1) if self._cur > 0 else (len(self._stack) - 1)
  183. '''
  184. else:
  185. code += '''
  186. return (%d,)
  187. ''' % ip
  188. code += '''
  189. return self._stack[self._cur]
  190. '''
  191. g = {}
  192. l = {}
  193. exec(code, g, l)
  194. self.__expr_fun = l['expr_fun']
  195. @property
  196. def shead(self):
  197. ''' @brief Stack head attribute '''
  198. return self._stack[self._cur]
  199. def _pop(self):
  200. ''' @brief Pops a value from the stack
  201. @note If stack head is 0, set the stack head to len(self._stack)
  202. - 1
  203. @return popped value
  204. '''
  205. res = self._stack[self._cur]
  206. if self._cur <= 0:
  207. self._cur = len(self._stack) - 1
  208. else:
  209. self._cur -= 1
  210. return res
  211. def _push(self, val):
  212. ''' @brief Pushes a value on the stack
  213. @note If no space left on the stack, the head is set to 0
  214. @param val int : value to push
  215. '''
  216. self._cur += 1
  217. if self._cur >= len(self._stack) or abs(self._cur) >= len(self._stack):
  218. self._cur = 0
  219. self._stack[self._cur] = self._cast_int(val)
  220. def _cast_int(self, val):
  221. ''' @brief Transform integer given Turmit max int and signed_int
  222. attr
  223. @param val int : integer to transform
  224. @return integer
  225. '''
  226. val = int(val)
  227. if self._max_int is not None:
  228. val %= self._max_int
  229. if not self._signed_int and val < 0:
  230. val = self._max_int - val
  231. return val
  232. @RpnOp
  233. def mem_sz(self, new_sz):
  234. ''' @brief Update memory stack size
  235. '''
  236. stksz = len(self._stack)
  237. new_sz %= 0xFFF
  238. if new_sz < 2:
  239. new_sz = 2
  240. if new_sz > stksz:
  241. self._stack += [ 0 for _ in range(new_sz - stksz) ]
  242. elif new_sz < stksz:
  243. self._stack = self._stack[0:new_sz]
  244. if self._cur >= new_sz:
  245. self._cur = 0
  246. @RpnOp
  247. def add(self, a, b):
  248. ''' @brief Adds one value to another
  249. @param a int : value
  250. @param b int : value to add to a
  251. @return a + b
  252. '''
  253. return a + b
  254. @RpnOp
  255. def sub(self, a, b):
  256. ''' @brief Substitutes a value to another
  257. @param a int : value
  258. @param b int : value to substitute to a
  259. @return a - b
  260. '''
  261. return a - b
  262. @RpnOp
  263. def bin_and(self, a, b):
  264. ''' @brief Binary and
  265. @param a int : value
  266. @param b int : value
  267. @return a & b
  268. '''
  269. return a & b
  270. @RpnOp
  271. def dup(self, a):
  272. ''' @brief Duplicates stack head
  273. @param a int : value
  274. @return a
  275. '''
  276. self._push(a)
  277. return a
  278. @RpnOp
  279. def lshift(self, a, b):
  280. ''' @brief Left shifts a of b
  281. @param a int : value
  282. @param b int : value
  283. @return a << b
  284. '''
  285. return a << b
  286. @RpnOp
  287. def mod(self, a, b):
  288. ''' @brief Gives a modulo b
  289. @param a int : value
  290. @param b int : value
  291. @return a % b
  292. '''
  293. return a % b
  294. @RpnOp
  295. def mul(self, a, b):
  296. ''' @brief Multiplies a with b
  297. @param a int : value
  298. @param b int : value
  299. @return a * b
  300. '''
  301. return a * b
  302. @RpnOp
  303. def div(self, a, b):
  304. ''' @brief Divides a with b
  305. @param a int : value
  306. @param b int : value
  307. @return a // b
  308. '''
  309. return a // b
  310. @RpnOp
  311. def bin_or(self, a, b):
  312. ''' @brief Binary or
  313. @param a int : value
  314. @param b int : value
  315. @return a | b
  316. '''
  317. return a | b
  318. @RpnOp
  319. def bin_xor(self, a, b):
  320. ''' @brief Binary xor
  321. @param a int : value
  322. @param b int : value
  323. @return a ^ b
  324. '''
  325. return a ^ b
  326. @RpnOp
  327. def pop(self, a):
  328. ''' @brief Pops a, head of the stack
  329. @param a int : value
  330. '''
  331. pass
  332. @RpnOp
  333. def swp(self, a, b):
  334. ''' @brief Swap the two values on top of the stack
  335. @param a int : v1
  336. @param b int : v2
  337. '''
  338. self._push(b)
  339. self._push(a)
  340. @RpnOp
  341. def jmp(self, offset):
  342. ''' @brief Increments IP
  343. @param offset int : jump offset symbols
  344. '''
  345. self.__ip += offset
  346. @RpnOp
  347. def jz(self, a, offset):
  348. ''' @brief Increments IP if a == 0
  349. @param a int : operand
  350. @param offset int : jump offset symbols
  351. '''
  352. if a == 0:
  353. self.__ip += offset
  354. """
  355. @RpnOp
  356. def jnz(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 jcmp(self, a, b, cnd, offset):
  366. ''' @brief Increments IP if a == b
  367. Condition type is encoded on an int :
  368. - 0b0000 : je
  369. - 0b0001 : jne
  370. - 0b0010 : jle
  371. - 0b0011 : jl
  372. - 0b0100 : jg
  373. - 0b0101 : jge
  374. - 0b0110 : jne
  375. - 0b0111 : jmp
  376. @param a int : operand
  377. @param b int : operand
  378. @param cnd int : condition type
  379. @param offset int : jump offset symbols
  380. '''
  381. jmp = False
  382. if cnd & 1 == 0 and a == b or cnd & 1 and a != b or\
  383. cnd & 0b10 and a < b or cnd & 0b100 and a > b:
  384. self.__ip += offset