ソースコード
"""
人工意識参号 - 現象的意識の最小モデル
第1.0版 2025.3.22 初版
MIT License
Copyright (c) 2025 Current Color Co. Ltd.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
import pygame
import pygame.midi
import numpy as np
import math
import random
# グローバル定数
N_MAX = 30 #最大層(底辺ノードの数)
N_CYCLE = 20 #入力情報のサイクル
C_MAX_IMAGE = 10 #入力画像の最大値
THRESHOLD = 10 # ノードの発火閾値
INFO_MAX = 13 #ノードの最大蓄積情報量
DECAY_RATE = 0.92 # ノード情報の通常減衰率
DISCHARGE_RATE = 0.6 # ノード情報の強制放電率
FATIGUE_MAX = 3 # ノードの疲労最大値
STRENGTHEN_RATE = 1.3 # リンク強化率
LINK_MIN = 0.28 # リンク強度の最低保証値
LINK_MAX = 1.28 #リンク強度の最大値
LINK_SUM_MAX = 2.1 # リンク強度の合計の最大値(正規化)
WEAKEN_RATE = 0.9999 # リンクの減衰率
FORGET_RATE = 0.8 # リンクの強制忘却率
INPUT_RATE = 1.0 #入力情報の入力ノード加算率
DECAY_RATE_OUTPUT = 0.9 # 出力ノードの情報減衰
C_RANDOM_TIMES = 3 #ランダム刺激の一回あたり発生数
#ウィンドウ
W_WIDTH = 1000
W_HEIGHT = 700
#シミュレーション領域
G_WIDTH = 600
G_HEIGHT = 700
G_UNIT = G_WIDTH / N_MAX
G_HIT = G_UNIT
C_QUALIA_LINE = G_HEIGHT * 0.25 #クオリア線のY座標
C_QUALIA_AREA = G_HEIGHT * 0.15 #クオリア発生域の幅
#インプット情報領域
IN_X = 600
IN_Y = 50
IN_WIDTH = 300
IN_HEIGHT = 200
IN_N = N_MAX
IN_M = N_CYCLE
#アウトプット情報領域
OUT_X = 600
OUT_Y = 260
OUT_WIDTH = 300
OUT_HEIGHT = 400
OUT_N = N_MAX
OUT_M = N_CYCLE * 2
# グローバル変数
time = 0
nodes = []
input_nodes = []
output_nodes = []
links = []
qualia = []
inputting = False
inputting_random = False
osound = False
qsound = False
ac_time = 0
#################################
# ノードクラス
class Node:
def __init__(self, x, y):
self.info = 0
self.info_next = self.info
self.state = 1
self.state_next = self.state
self.threshold = THRESHOLD
self.fatigue = 0
self.in_links = []
self.out_links = []
self.max_links = 10
self.x, self.y = x, y
self.clicked = False
def add_info(self, amount):
self.info_next += amount
self.info_next = min(self.info_next, INFO_MAX)
#接続リンク群の強度正規化
def normalize(self, links, sum_max):
sum = 0
for link in links:
if link.fixed == False:
sum += link.get_strength()
if sum > 0:
norm_rate = min(sum, LINK_MAX * sum_max) / sum
for link in links:
if link.fixed == False:
link.set_strength(link.get_strength() * norm_rate)
#状態遷移
def exec(self):
# 無反応期
if self.state == 0:
self.normalize(self.in_links, LINK_SUM_MAX)
self.normalize(self.out_links, LINK_SUM_MAX)
self.fatigue = 0
self.state_next = 1
# 通常期
elif self.state == 1:
if self.info >= self.threshold:
for link in self.out_links:
x = self.info * link.get_strength()
if link.dest.state == 1 or link.dest.state == 3:
link.dest.add_info(x)
self.state_next = 2
# 発火期
elif self.state == 2:
self.info_next = 0
self.fatigue += 1
self.state_next = 1 if self.fatigue < FATIGUE_MAX else 0
def discharge(self):
self.info = self.info * DISCHARGE_RATE
self.info_next = self.info
def commit(self):
self.info = self.info_next
self.state = self.state_next
if self.state == 1:
self.info = self.info * DECAY_RATE
self.info_next = self.info
elif self.state == 3:
self.info = self.info * DECAY_RATE_OUTPUT
self.info_next = self.info
def get_state(self):
return self.state
def set_state(self, state):
self.state = state
self.state_next = self.state
def add_outbound_link(self, link):
if len(self.out_links) < self.max_links:
self.out_links.append(link)
def add_inbound_link(self, link):
if len(self.in_links) < self.max_links:
self.in_links.append(link)
def get_info(self):
return self.info
#描画処理
def draw_node(self):
color = (255, 0, 0) if self.state == 2 else (255, 255, 255)
pygame.draw.circle(screen, color, (int(self.x), int(self.y)), max(5,self.info*0.8))
def draw_inode(self):
color = (255, 0, 0) if self.state == 2 else (0, 128, 255)
pygame.draw.circle(screen, color, (int(self.x), int(self.y)), max(5,self.info))
if self.clicked:
pygame.draw.circle(screen, (255, 255, 0), (int(self.x), int(self.y)), 10, 4)
self.clicked = False
def draw_onode(self):
color = (255, 0, 0) if self.state == 2 else (255, 165, 0)
pygame.draw.circle(screen, color, (int(self.x), int(self.y)), max(5,self.info))
#################################
# リンククラス
class Link:
def __init__(self, src, dest):
self.src = src
self.dest = dest
self.strength = LINK_MIN # 伝播力
self.strength_next = self.strength
self.fixed = False
def get_strength(self):
return self.strength
def set_strength(self, str):
self.strength = str
self.strength_next = self.strength
return
def fix(self):
self.strength = 1
self.strength_next = self.strength
self.fixed = True
def exec(self):
global qualia
if self.src.get_state() == 2 and self.dest.get_state() == 2:
#ヘブ則によるリンク強化
self.strength_next = min(LINK_MAX, self.strength * STRENGTHEN_RATE)
#クオリアの発生判定
if self.dest.y < self.src.y:
qy = (self.src.y + self.dest.y) / 2
if ( qy > C_QUALIA_LINE - C_QUALIA_AREA and
qy < C_QUALIA_LINE + C_QUALIA_AREA ):
str = self.strength * ((C_QUALIA_AREA - abs(qy - C_QUALIA_LINE) ) /
C_QUALIA_AREA)
quale = Quale((self.src.x + self.dest.x)/2,
(self.src.y + self.dest.y)/2,
str)
qualia.append(quale)
elif self.fixed == False:
#リンクの弱化
self.strength = self.strength * WEAKEN_RATE
self.strength_next = max(self.strength, LINK_MIN)
def forget(self):
if self.fixed == False:
#強制忘却
self.strength = self.strength * FORGET_RATE
self.strength_next = max(self.strength, LINK_MIN)
def commit(self):
self.strength = self.strength_next
def draw(self, screen):
Draw.parallel_line(screen, (128, 128, 128),
(int(self.src.x), int(self.src.y)),
(int(self.dest.x), int(self.dest.y)),
int(self.strength * 6 / LINK_MAX), 3)
#################################
# 描画ツールクラス
class Draw:
#平行移動した線を描画
def parallel_line(screen, color, start_point, end_point, width, distance):
# distance -- 平行移動する距離(正の値なら左側、負の値なら右側)
# 始点と終点の座標を取得
x1, y1 = start_point
x2, y2 = end_point
# 線の方向ベクトルを計算
dx = x2 - x1
dy = y2 - y1
# ベクトルの長さを計算
length = math.sqrt(dx**2 + dy**2)
# 長さがゼロの場合は描画しない(点になるため)
if length == 0:
return
# 単位ベクトルを計算
unit_dx = dx / length
unit_dy = dy / length
# 90度回転させた単位ベクトル(法線ベクトル)を計算
# 左向きの法線ベクトル
normal_dx = -unit_dy
normal_dy = unit_dx
# 平行移動の距離を適用
offset_x = distance * normal_dx
offset_y = distance * normal_dy
# 平行移動した始点と終点を計算
parallel_x1 = x1 + offset_x
parallel_y1 = y1 + offset_y
parallel_x2 = x2 + offset_x
parallel_y2 = y2 + offset_y
# 平行移動した線を描画
pygame.draw.line(screen, color, (parallel_x1, parallel_y1), (parallel_x2, parallel_y2), width)
return None
#################################
# 入力情報クラス(右上部)
class InputInfo:
#生成
def __init__(self):
# 入力情報(モザイク画像)用の2次元配列を作成
self.input_array = np.zeros((IN_M, IN_N))
# 入力情報のポインタ
self.pointer = -1
# 画像ファイルの読み込み
def read_input_image(self, num):
try:
image_path = "image"+str(num)+".jpg"
original_image = pygame.image.load(image_path)
except pygame.error as e:
print(f"画像の読み込みに失敗しました: {e}")
return
# 画像のサイズを取得
width_S = original_image.get_width()
height_S = original_image.get_height()
# トリミング領域のサイズを計算
source_ratio = width_S / height_S
target_ratio = IN_WIDTH / IN_HEIGHT
if source_ratio > target_ratio:
# 元画像が横長の場合
height_T = height_S
width_T = int(height_S * target_ratio)
else:
# 元画像が縦長の場合
width_T = width_S
height_T = int(width_S / target_ratio)
# トリミング開始位置の計算(画像の中央からトリミング)
x_offset = (width_S - width_T) // 2
y_offset = (height_S - height_T) // 2
# 画像のピクセル情報を取得
pixel_array = pygame.surfarray.array3d(original_image)
# サンプリングしてグレースケール値を計算し二次元配列に格納
for j in range(IN_M):
for i in range(IN_N):
x = x_offset + int(width_T * i / IN_N)
y = y_offset + int(height_T * j / IN_M)
# 画像の範囲内かチェック
if 0 <= x < width_S and 0 <= y < height_S:
r, g, b = pixel_array[x, y]
# RGBをグレースケールに変換
gray = r * 0.2989 + g * 0.5870 + b * 0.1140
self.input_array[j, i] = gray
# 入力ファイルの表示
def draw_input_image(self, screen):
# モザイク状の画像を描画
rect_width = IN_WIDTH // IN_N
rect_height = IN_HEIGHT // IN_M
for j in range(IN_M):
for i in range(IN_N):
# モザイク1つ分の矩形を計算
rect_x = IN_X + i * IN_WIDTH // IN_N
rect_y = IN_Y + j * IN_HEIGHT // IN_M
# グレースケール値を取得して塗りつぶし色を決定
gray_value = int(self.input_array[j, i])
color = (gray_value, gray_value, gray_value)
# 矩形を描画
pygame.draw.rect(screen, color, (rect_x, rect_y, rect_width, rect_height))
# 入力場所の表示と更新
def draw_input_line(self, screen):
global inputting
#表示
if inputting == True:
y = IN_Y + (self.pointer + 0.5) * IN_HEIGHT // IN_M
pygame.draw.line(screen, (255,0,0),
(IN_X, y), (IN_X + IN_WIDTH, y), 1)
#更新
self.pointer += 1
if self.pointer >= N_CYCLE:
self.pointer = 0
# 入力情報の返却
def get_input(self, i):
return self.input_array[self.pointer, i] / 256
#################################
# 出力情報クラス(右下部)
class OutputInfo:
#生成
def __init__(self):
# 出力情報用の2次元配列を作成
self.output_array = np.zeros((OUT_M, OUT_N))
# 出力情報のポインタ
self.pointer = 0
# 出力履歴の描画
def draw_output_history(self, screen):
rect_width = OUT_WIDTH // OUT_N
rect_height = OUT_HEIGHT // OUT_M
for j in range(OUT_M):
k = (self.pointer + j + 1) % OUT_M
rect_y = OUT_Y + j * OUT_HEIGHT // OUT_M
for i in range(OUT_N):
rect_x = OUT_X + i * OUT_WIDTH // OUT_N
gray_value = int(self.output_array[k, i])
color = (gray_value, gray_value, gray_value)
pygame.draw.rect(screen, color, (rect_x, rect_y, rect_width, rect_height))
self.pointer += 1
if self.pointer >= OUT_M:
self.pointer = 0
# 出力情報の設定
def set_output(self, i, info):
self.output_array[self.pointer, i] = min(255, info / INFO_MAX * 256)
#################################
# クオリアクラス
class Quale:
C_color_quale = (128, 230, 255)
#生成
def __init__(self, x, y, intensity):
self.intensity = intensity
self.x = int(x)
self.y = int(y)
self.cnt = int(intensity * 8)+1
self.rad = 0
self.stat = 2 #初期状態
#実行
def exec(self):
self.cnt -= 1
if self.cnt <= 0:
self.stat = 0 #消滅
elif self.rad > 0:
self.stat = 1 #通常状態
self.rad += 3
return self.stat
#描画
def draw(self, screen):
pygame.draw.circle(screen, self.C_color_quale,
(self.x, self.y), self.rad, self.cnt)
#################################
# ボタンクラス
class Button:
C_color_button = (218, 218, 218)
C_color_button_clicked = (255, 80, 0)
C_color_button_text = (20, 20, 20)
C_clicked_disp = 5
#生成
def __init__(self, x,y,w,h,xt,yt,txt):
self.rec = pygame.Rect(x,y,w,h)
self.pos = (xt, yt)
self.txt = txt
self.clicked = 0
self.font = pygame.font.SysFont(None, 25)
#表示
def draw(self,screen):
color = self.C_color_button_clicked if self.clicked > 0 else self.C_color_button
pygame.draw.rect(screen, color, self.rec)
text = self.font.render(self.txt, False, self.C_color_button_text)
screen.blit(text, self.pos)
if self.clicked > 0:
self.clicked -= 1
#クリック判定
def collide(self, pos):
if self.rec.collidepoint(pos):
self.clicked = self.C_clicked_disp
return True
else:
return False
#################################
# UIクラス
class UI:
C_color_text = (218, 218, 218)
t_num_pos = (145,15)
t_input_pos = (97,45)
t_random_pos = (97,105)
t_osound_pos = (97,135)
t_qsound_pos = (97,165)
t_time_txt_pos = (400,15)
t_time_pos = (450,15)
#生成
def __init__(self):
self.input_image = 0
self.selecting = False
self.font = pygame.font.SysFont(None, 25)
self.b_down = Button(10,10,60,27, 13,15, "DOWN")
self.b_up = Button(80,10,60,27, 98,15, "UP")
self.b_set = Button(180,10,60,27, 195,15, "SET")
self.b_input = Button(10,40,82,27, 25,45, "INPUT")
self.b_discharge = Button(10,70,110,27, 15,75, "DISCHARGE")
self.b_forget = Button(130,70,90,27, 140,75, "FORGET")
self.b_random = Button(10,100,82,27, 12,105, "RANDOM")
self.b_osound = Button(10,130,82,27, 15,135, "oSOUND")
self.b_qsound = Button(10,160,82,27, 15,165, "qSOUND")
# UIの表示
def draw(self, screen):
global osound, qsound, ac_gtime
self.b_up.draw(screen)
self.b_down.draw(screen)
self.b_set.draw(screen)
self.b_input.draw(screen)
self.b_discharge.draw(screen)
self.b_forget.draw(screen)
self.b_osound.draw(screen)
self.b_random.draw(screen)
self.b_qsound.draw(screen)
txt = self.font.render("TIME", False, self.C_color_text)
screen.blit(txt, self.t_time_txt_pos)
txt = self.font.render(str(ac_time), False, self.C_color_text)
screen.blit(txt, self.t_time_pos)
if self.selecting == True:
txt = self.font.render(str(self.input_image), False, self.C_color_text)
screen.blit(txt, self.t_num_pos)
txt = "ON" if osound==True else "OFF"
fnt = self.font.render(txt, False, self.C_color_text)
screen.blit(fnt, self.t_osound_pos)
txt = "ON" if qsound==True else "OFF"
fnt = self.font.render(txt, False, self.C_color_text)
screen.blit(fnt, self.t_qsound_pos)
txt = "ON" if inputting==True else "OFF"
fnt = self.font.render(txt, False, self.C_color_text)
screen.blit(fnt, self.t_input_pos)
txt = "ON" if inputting_random==True else "OFF"
fnt = self.font.render(txt, False, self.C_color_text)
screen.blit(fnt, self.t_random_pos)
# ボタン押下処理
def click(self, event, input_info):
global inputting, nodes, links, osound, qsound, player, inputting_random
if self.b_up.collide(event.pos):
self.selecting = True
self.input_image += 1
if self.input_image > C_MAX_IMAGE:
self.input_image = 0
if self.b_down.collide(event.pos):
self.selecting = True
self.input_image -= 1
if self.input_image < 0:
self.input_image = C_MAX_IMAGE
if self.b_set.collide(event.pos):
self.selecting = False
input_info.read_input_image(self.input_image)
if self.b_input.collide(event.pos):
inputting = True if inputting == False else False
if self.b_discharge.collide(event.pos):
for node in nodes:
node.discharge()
if self.b_forget.collide(event.pos):
for link in links:
link.forget()
if self.b_osound.collide(event.pos):
if osound == True:
osound = False
player.stop_osound()
else:
osound = True
if self.b_qsound.collide(event.pos):
if qsound == True:
qsound = False
player.stop_qsound()
else:
qsound = True
if self.b_random.collide(event.pos):
inputting_random = True if inputting_random == False else False
#################################
# 音声クラス
class Player:
CHANNEL_1 = 0
CHANNEL_2 = 1
INSTRUMENT_1 = 82
INSTRUMENT_2 = 0
#生成
def __init__(self):
pygame.midi.init()
self.midi = pygame.midi.Output(pygame.midi.get_default_output_id())
self.midi.set_instrument(self.INSTRUMENT_1, self.CHANNEL_1)
self.midi.set_instrument(self.INSTRUMENT_2, self.CHANNEL_2)
# 出力情報の音声化
def play_output(self, output_nodes):
for onode in output_nodes:
note = int((onode.x - G_WIDTH / 2) / G_UNIT ) * 2 +60
vol = int((127 - (INFO_MAX - onode.info)*10) * 0.5)
if onode.info > INFO_MAX * 0.85:
self.midi.note_on(note, vol, self.CHANNEL_1)
if onode.info < INFO_MAX * 0.8:
self.midi.note_off(note, vol, self.CHANNEL_1)
# クオリア音の開始
def quale_on(self, quale):
note = int((quale.x - G_WIDTH / 2) / G_UNIT ) * 2 +60
if quale.intensity > 0.8:
vol = int(quale.intensity * 127)
self.midi.note_on(note, vol, self.CHANNEL_2)
# クオリア音の終了
def quale_off(self, quale):
note = int((quale.x - G_WIDTH / 2) / G_UNIT ) * 2 +60
if quale.intensity > 0.8:
vol = int(quale.intensity * 127)
self.midi.note_off(note, vol, self.CHANNEL_2)
# 音声の停止
def stop_osound(self):
self.midi.write_short(0xB0, 0x7B, 0x00)
def stop_qsound(self):
self.midi.write_short(0xB1, 0x7B, 0x00)
#################################
# 初期化
def ac3_init():
global nodes, input_nodes, output_nodes, links,top_node
# 三角形配置(層ごとにノードを管理)
node_layers = [[] for _ in range(N_MAX + 1)] # 0層は未使用、1~Nmax層を使用
for layer in range(N_MAX, 0, -1): # 底辺(N_MAX)から頂点(1)へ
for i in range(layer):
x = G_WIDTH / 2 - (layer - 1) * G_UNIT / 2 + i * G_UNIT
y = G_HEIGHT - 100 - (N_MAX - layer) * G_UNIT
node = Node(x, y)
nodes.append(node)
node_layers[layer].append(node)
# リンク生成
# 抽象化方向リンク
# N層i番目 → N-1層i-1番目(i>0)と N-1層i番目(i≦N-1)
for layer in range(N_MAX, 1, -1):
for i, src_node in enumerate(node_layers[layer]):
if i > 0:
dest_node = node_layers[layer - 1][i-1]
make_link(src_node, dest_node)
if i < len(node_layers[layer - 1]):
dest_node = node_layers[layer - 1][i]
make_link(src_node, dest_node)
# 具体化方向リンク
# N層i番目 → N+1層i番目 と N+1層i+1番目
for layer in range(N_MAX-1, 0, -1):
for i, src_node in enumerate(node_layers[layer]):
dest_node = node_layers[layer + 1][i]
make_link(src_node, dest_node)
dest_node = node_layers[layer + 1][i+1]
make_link(src_node, dest_node)
# 同層内リンク
# N層i番目 → N層i-1番目(i<0の時N番目)とN層i+1番目(i>Nの時0番目)
for layer in range(N_MAX, 1, -1):
for i, src_node in enumerate(node_layers[layer]):
#左隣
if i < 0:
dest_node = node_layers[layer][layer-1]
else:
dest_node = node_layers[layer][i-1]
make_link(src_node, dest_node)
#右隣
if i >= layer-1:
dest_node = node_layers[layer][0]
else:
dest_node = node_layers[layer][i+1]
make_link(src_node, dest_node)
# 入力ノード(底辺ノードの真下に描画)
input_nodes = [Node(node_layers[N_MAX][i].x, G_HEIGHT - 80) for i in range(N_MAX)]
for i, inode in enumerate(input_nodes):
dest_node = node_layers[N_MAX][i]
make_fix_link(inode, dest_node)
# 出力ノード(入力ノードの真下に描画)
output_nodes = [Node(node_layers[N_MAX][i].x, G_HEIGHT - 60) for i in range(N_MAX)]
for i, onode in enumerate(output_nodes):
onode.set_state(3)
src_node = node_layers[N_MAX][i]
make_fix_link(src_node, onode)
return None
def make_link(src_node, dest_node):
link = Link(src_node, dest_node)
src_node.add_outbound_link(link)
dest_node.add_inbound_link(link)
links.append(link)
def make_fix_link(src_node, dest_node):
link = Link(src_node, dest_node)
link.fix()
src_node.add_outbound_link(link)
dest_node.add_inbound_link(link)
links.append(link)
#################################
# 主処理
#初期化
pygame.init()
screen = pygame.display.set_mode((W_WIDTH, W_HEIGHT))
clock = pygame.time.Clock()
ac3_init()
print("Num of Nodes:", len(nodes))
print("Num of Links:",len(links))
input_info = InputInfo()
input_info.read_input_image(0)
output_info = OutputInfo()
ui = UI()
player = Player()
running = True
input_active = True
osound = False
qsound = False
#メインループ
while running:
#イベント処理
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEMOTION and input_active:
mx, my = pygame.mouse.get_pos()
for inode in input_nodes:
dist = ((inode.x - mx) ** 2 + (inode.y - my) ** 2) ** 0.5
if dist < G_HIT:
inode.add_info(INFO_MAX)
inode.clicked = True
if event.type == pygame.MOUSEBUTTONDOWN:
ui.click(event, input_info)
# 更新処理
if inputting == True:
for i, inode in enumerate(input_nodes):
inode.add_info(INFO_MAX * input_info.get_input(i) * INPUT_RATE)
if inputting_random == True:
for _ in range(C_RANDOM_TIMES):
i = random.randint(0, len(nodes) - 1)
nodes[i].add_info(INFO_MAX)
for i, onode in enumerate(output_nodes):
output_info.set_output(i, onode.get_info())
all_nodes = input_nodes + nodes + output_nodes
for node in all_nodes:
node.exec()
for link in links:
link.exec()
for quale in qualia:
ret = quale.exec()
if ret == 0:
# if qsound == True:
# player.quale_off(quale)
qualia.remove(quale)
if ret == 2:
if qsound == True:
player.quale_on(quale)
# 次の計算ステップへの移行
all_nodes = input_nodes + nodes + output_nodes
for node in all_nodes:
node.commit()
for link in links:
link.commit()
# 描画処理
screen.fill((10, 10, 10))
input_info.draw_input_image(screen)
input_info.draw_input_line(screen)
output_info.draw_output_history(screen)
ui.draw(screen)
for link in links:
link.draw(screen)
for node in nodes:
node.draw_node()
for inode in input_nodes:
inode.draw_inode()
for onode in output_nodes:
onode.draw_onode()
for quale in qualia:
quale.draw(screen)
# 音声処理
if osound == True:
player.play_output(output_nodes)
ac_time += 1
pygame.display.flip()
clock.tick(30)
pygame.quit()