mitsuyoshi-yamazaki / ALifeGameJam2019

ALife Art "BlindPainter"
https://mitsuyoshi-yamazaki.github.io/ALifeGameJam2019/
MIT License
9 stars 3 forks source link

Machines and Tapes #90

Closed mitsuyoshi-yamazaki closed 4 years ago

mitsuyoshi-yamazaki commented 4 years ago

課題

アイデア

はじめに

ALife Core

前身の ALife Core では、収束しない進化を実現するため、自己と他者の関係性を淘汰圧として実装した。 具体的には、遺伝子を自己を示す部分(prey-part)と餌を示す部分(pred-part)のふたつに分け、接触した他者のprey-partが自己のpred-partと一致すれば捕食する実装を行なった。

これによりとりうる自己-他者の関係性は以下の4パターンである。

自己 他者
無関係 無関係
被食者 捕食者
捕食者 被食者
捕食者 捕食者

ある時点でどのような関係の他者が周囲に存在するかは異なるためその時々で有利となる遺伝形質も異なり、これにより収束しない進化を実現した。

ALife Core の課題

前述のように ALife Core ではprey-partとpred-partのふたつの部分からなる遺伝子を実装したが、これは標識となる情報であるだけで、それ以外に何の働きももたない。 遺伝子の情報はそれをもつ生命個体にとって何の意味もなく、他者との関係性が存在して初めて意味を持つようになるものである。

今回の施策では遺伝子が「次代をコードする情報」と「どの相手と番うか」の両方の情報を重ねて保持する実装を行う。

補足

遺伝子を可変長にするなどの変更を実装することもできるが、自己複製機能に遊びをもたせると複製効率の良い遺伝子に収束する恐れがある(マッチングしやすい巨大な遺伝子になるなど) 今回も環境に対する適応性ではなく他者との関係性に注目するためそのような仕様にはしない。

構造

4 bit の head と n bit の transition table からなる 4 + n bit 長の遺伝子 ある遺伝子の head と別の遺伝子の任意の連続した 4 bit の bit が全て異なれば再生産可能 head にマッチした直後の bit から transition table の XOR をとった値が次代の遺伝子となる transition table は遺伝子長と比べて head の分 bit が足りないので、transition table の先頭 4 bit は使いまわされる

01011111 という遺伝子Aと 11010100 という遺伝子Bが子孫を残す場合を例にとると、

遺伝子 A の head Ah0101, transition table At1111 Ah が遺伝子 B に対してマッチする部分を探索すると、1bit目と3bit目が見つかる(現在の実装では、探索開始インデックスはランダム) Ah が1bit目にヒットした場合、 At が読み込むのは B の1bit目から head 長分後ろの5bit目からとなる。すなわち5bitシフトして B’ 10011010 これを At が読み込むのだが、 transition table 長は遺伝子長より短いので transition table の前半を使い回して At’ 11111111 を用いる At’B’ のXORをとった 11111111 ^ 10011010 = 01100101 が次代の遺伝子となる

実装仕様

個体の死について

自己複製遺伝子の探索

head: 1111, transition table: 0 * n の遺伝子は自己複製可能

# 結果
>>> check_all(8)
[('11110000', 0)]
>>> check_all(9)
[('111100000', 0)]
>>> check_all(10)
[('1100111100', 2), ('1111000000', 0)]
>>> check_all(11)
[('11110000000', 0)]
>>> check_all(12)
[('111100000000', 0)]
>>> check_all(13)
[('0101100100011', 3), ('1111000000000', 0)]
>>> check_all(14)
[('11001100001111', 6), ('11110000000000', 0)]
>>> check_all(15)
[('111100000000000', 0)]
>>> check_all(16)
[('1100111100111100', 2), ('1111000000000000', 0)]
# python
def create(l):
  return [format(i, '0%sb' % l) for i in xrange(pow(2, l))]

header = 4

def check_header(b, i):
  l = len(b)
  for j in xrange(header):
    if int(b[j]) ^ int(b[(i - 4 + j) % l]) != 1:
      return False
  return True

DEBUG = True
def log(s):
  if DEBUG == True:
    print(s)

def check_replication(b, i):
  l = len(b)
  transaction = l - header
  log('l: {}, h: {}, t: {}'.format(l, header, transaction))
  for j in xrange(l):
    t = b[header + (j % transaction)]
    c = b[(i + j) % l]
    e = b[j]
    log('{}: {}({}) ^ {}({}) ? {}({})'.format(j, t, header + (j % transaction), c, (i + j) % l, e, j))
    if int(t) ^ int(c) != int(e):
      return False
  return True

def check(b, i):
  if check_header(b, i) == False:
    return False
  return check_replication(b, i)

def check_all(l):
  result = []
  a = create(l)
  for b in a:
    for i in xrange(l):
      if check(b, i) == True:
        result.append((b, i))
  return result

自分とマッチできる遺伝子の探索

# 結果
>>> r = check_all(10)
>>> len(r)
444
# 10bitに遺伝子においては444通り
# python
DEBUG = True
header = 4

def log(s):
  if DEBUG == True:
    print(s)

def create(l):
  return [format(i, '0%sb' % l) for i in xrange(pow(2, l))]

def check_header(b, i):
  l = len(b)
  for j in xrange(header):
    if int(b[j]) ^ int(b[(i - 4 + j) % l]) != 1:
      return False
  return True

def check_all(l):
  result = {}
  a = create(l)
  for b in a:
    r = []
    for i in xrange(l):
      if check_header(b, i) == True:
        r.append(i)
    if len(r) > 0:
      result[b] = r
  return result

子孫全探索

「子孫を残せる相手の種類」が4パターンしかない

# 結果
>>> z = check_all(10)
>>> len(z)
1024
>>> a = create(10)
>>> r = []
>>> for b in a:
...   zz = z[b]
...   if len(zz) not in r:
...     r.append(len(zz))
... 
>>> r
[316, 580, 510, 462]
# python
DEBUG = True
header = 4

def log(s):
  if DEBUG == True:
    print(s)

def create(l):
  return [format(i, '0%sb' % l) for i in xrange(pow(2, l))]

def check_header(h, other, i):
  l = len(other)
  for j in xrange(header):
    if int(h[j]) ^ int(other[(i - 4 + j) % l]) != 1:
      return False
  return True

def child(t, other, i):
  l = len(other)
  m = pow(2, l) - 1
  n = int(other, 2)
  shifted = (((n << l) + n) >> (l - i)) & m
  binary = int(t, 2) ^ shifted
  return  format(binary, '0%sb' % l)

def check_all(l):
  transition = l - header
  result = {}
  a = create(l)
  ii = 0
  for b in a:
    if (ii % 10000) == 0:
      log("{} genes".format(ii))
    ii += 1
    r = {}
    t = b[header:l] + b[header:(header * 2)]
    for other in a:
      rr = []
      for i in xrange(l):
        if check_header(b, other, i) == True:
          rr.append(child(t, other, i))
      if len(rr) > 0:
        r[other] = rr
    if len(r) > 0:
      result[b] = r
  return result

refs

Active Mutation in Self-Reproducing Networks of Machines and Tapes

パラメータ

遺伝子

Gallery

https://twitter.com/vespid/status/1279120617237655552?s=20

1593872081__00001385 mode=attracted


1593876464__00002537 mode=scroll 世代の波が見える


1593796377__00001378

1593801304__00000579

1593796645__00001495

1593812634__00001196 Gene Length を 12 に 差異は感じられない


1593961956__00000557 mode=family, art_mode=1

その他

mitsuyoshi-yamazaki commented 4 years ago

ページ内にこのPRへのリンクを貼ったのでマージして閉じる