From 4eb29ecb56ef1c1815e4615ca51e32d32a2e01eb Mon Sep 17 00:00:00 2001 From: Yamazaki Mitsuyoshi Date: Sun, 23 May 2021 14:02:04 +0900 Subject: [PATCH] =?UTF-8?q?=E8=89=B2=E5=88=86=E3=81=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/cellular_automata.js | 2 +- dist/hex_cellular_automata.js | 15 ++++++- dist/hex_cellular_automata_autosearch.js | 15 ++++++- package.json | 2 +- .../hex_cellular_automata/model.ts | 40 +++++++++++++------ src/simulations/hex_cellular_automata/rule.ts | 36 ++++++++++++----- 6 files changed, 80 insertions(+), 30 deletions(-) diff --git a/dist/cellular_automata.js b/dist/cellular_automata.js index ca687b8..8309cf4 100644 --- a/dist/cellular_automata.js +++ b/dist/cellular_automata.js @@ -1565,7 +1565,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"HydrophilicRule\": () => /* binding */ HydrophilicRule\n/* harmony export */ });\n/* harmony import */ var _classes_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../classes/color */ \"./src/classes/color.ts\");\n\nconst oil = 0;\nconst water = 1;\nconst membrane = 2;\nconst stateInfo = [\n {\n name: \"oil\",\n hydrophilic: -1,\n color: _classes_color__WEBPACK_IMPORTED_MODULE_0__.Color.white(0x0),\n },\n {\n name: \"water\",\n hydrophilic: 1,\n color: new _classes_color__WEBPACK_IMPORTED_MODULE_0__.Color(83, 150, 205),\n },\n {\n name: \"membrane\",\n hydrophilic: 0,\n color: new _classes_color__WEBPACK_IMPORTED_MODULE_0__.Color(0xFC, 0x88, 0x45),\n },\n];\nconst colorPalette = {\n toString: () => {\n return \"ColorPalette\";\n },\n colorOf: (state) => {\n return stateInfo[state].color;\n }\n};\nclass HydrophilicRule {\n constructor(radius) {\n this.radius = radius;\n this.numberOfStates = stateInfo.length;\n this.colorPalette = colorPalette;\n this._weights = [];\n if (radius <= 0) {\n throw new Error(`Invalid argument: radius should be > 0 (${radius})`);\n }\n for (let i = 0; i <= radius; i += 1) {\n this._weights.push(radius - i + 1);\n }\n let maxScore = 0;\n for (let i = 0; i <= radius; i += 1) {\n const score = radius - i + 1;\n const numberOfCells = Math.max(6 * i, 1);\n maxScore += score + numberOfCells;\n }\n this._maxScore = maxScore;\n }\n get weights() {\n return this._weights;\n }\n toString() {\n return `HydrophilicRule with ${this.colorPalette.toString()}`;\n }\n nextState(state, states) {\n states.increment(state, 1);\n // const unit = 6\n // const maxUnit = this.radius * (this.radius + 1) / 2\n let hydrophilic = 0;\n const oilCount = states.stateCount(oil);\n const waterCount = states.stateCount(water);\n const membraneCount = states.stateCount(membrane);\n hydrophilic += stateInfo[oil].hydrophilic * oilCount;\n hydrophilic += stateInfo[water].hydrophilic * waterCount;\n hydrophilic += stateInfo[membrane].hydrophilic * membraneCount;\n if (Math.abs(hydrophilic) < this._maxScore * 0.2) {\n return membrane;\n }\n // if (hydrophilic > this._maxScore * 0.8) {\n // return membrane\n // }\n return hydrophilic > 0 ? water : oil;\n }\n}\n\n\n//# sourceURL=webpack://alife-lab/./src/simulations/cellular_automata/rules/hydrophilic_rule.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"HydrophilicRule\": () => /* binding */ HydrophilicRule\n/* harmony export */ });\n/* harmony import */ var _classes_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../../classes/color */ \"./src/classes/color.ts\");\n\nconst oil = 0;\nconst water = 1;\nconst membrane = 2;\nconst stateInfo = [\n {\n name: \"oil\",\n hydrophilic: -1,\n color: _classes_color__WEBPACK_IMPORTED_MODULE_0__.Color.white(0x0),\n },\n {\n name: \"water\",\n hydrophilic: 1,\n color: new _classes_color__WEBPACK_IMPORTED_MODULE_0__.Color(83, 150, 205),\n },\n {\n name: \"membrane\",\n hydrophilic: 0,\n color: new _classes_color__WEBPACK_IMPORTED_MODULE_0__.Color(0xFC, 0x88, 0x45),\n },\n];\nconst colorPalette = {\n toString: () => {\n return \"ColorPalette\";\n },\n colorOf: (state) => {\n return stateInfo[state].color;\n }\n};\nclass HydrophilicRule {\n constructor(radius) {\n this.radius = radius;\n this.numberOfStates = stateInfo.length;\n this.colorPalette = colorPalette;\n this._weights = [];\n if (radius <= 0) {\n throw new Error(`Invalid argument: radius should be > 0 (${radius})`);\n }\n for (let i = 0; i <= radius; i += 1) {\n this._weights.push(radius - i + 1);\n }\n let maxScore = 0;\n for (let i = 0; i <= radius; i += 1) {\n const score = radius - i + 1;\n const numberOfCells = Math.max(6 * i, 1);\n maxScore += score + numberOfCells;\n }\n this._maxScore = maxScore;\n }\n get weights() {\n return this._weights;\n }\n toString() {\n return `HydrophilicRule with ${this.colorPalette.toString()}`;\n }\n nextState(state, states) {\n states.increment(state, 1);\n // const unit = 6\n // const maxUnit = this.radius * (this.radius + 1) / 2\n let hydrophilic = 0;\n const oilCount = states.stateCount(oil);\n const waterCount = states.stateCount(water);\n const membraneCount = states.stateCount(membrane);\n hydrophilic += stateInfo[oil].hydrophilic * oilCount;\n hydrophilic += stateInfo[water].hydrophilic * waterCount;\n hydrophilic += stateInfo[membrane].hydrophilic * membraneCount;\n if (Math.abs(hydrophilic) < this._maxScore * 0.2) {\n return membrane;\n }\n // ネットワークができる\n // if (hydrophilic > this._maxScore * 0.8) {\n // return membrane\n // }\n return hydrophilic > 0 ? water : oil;\n }\n}\n\n\n//# sourceURL=webpack://alife-lab/./src/simulations/cellular_automata/rules/hydrophilic_rule.ts?"); /***/ }), diff --git a/dist/hex_cellular_automata.js b/dist/hex_cellular_automata.js index cb2e294..35f772d 100644 --- a/dist/hex_cellular_automata.js +++ b/dist/hex_cellular_automata.js @@ -1371,6 +1371,17 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ }), +/***/ "./src/classes/color.ts": +/*!******************************!*\ + !*** ./src/classes/color.ts ***! + \******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"Color\": () => /* binding */ Color\n/* harmony export */ });\nclass Color {\n constructor(r, g, b, alpha) {\n this.r = r;\n this.g = g;\n this.b = b;\n this.alpha = alpha !== null && alpha !== void 0 ? alpha : 0xFF;\n }\n static white(white, alpha) {\n return new Color(white !== null && white !== void 0 ? white : 0xFF, white !== null && white !== void 0 ? white : 0xFF, white !== null && white !== void 0 ? white : 0xFF, alpha !== null && alpha !== void 0 ? alpha : 0xFF);\n }\n p5(p, alpha) {\n return p.color(this.r, this.g, this.b, alpha !== null && alpha !== void 0 ? alpha : this.alpha);\n }\n toString() {\n const rawColorToString = (value) => {\n return value.toString(16)\n .padStart(2, \"0\");\n };\n return `#${rawColorToString(this.r)}${rawColorToString(this.g)}${rawColorToString(this.b)}`;\n }\n}\n\n\n//# sourceURL=webpack://alife-lab/./src/classes/color.ts?"); + +/***/ }), + /***/ "./src/classes/downloader.tsx": /*!************************************!*\ !*** ./src/classes/downloader.tsx ***! @@ -1510,7 +1521,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var p5__ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"Model\": () => /* binding */ Model\n/* harmony export */ });\n/* harmony import */ var _classes_utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../classes/utilities */ \"./src/classes/utilities.ts\");\n/* harmony import */ var _classes_physics__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../classes/physics */ \"./src/classes/physics.ts\");\n\n\nclass Model {\n constructor(size, rule, initialState) {\n this.size = size;\n this.rule = rule;\n this._t = 0;\n this._state = createState(size, initialState);\n console.log(`rule: ${rule.rule}, size: ${size}`);\n }\n get t() {\n return this._t;\n }\n get state() {\n return this._state;\n }\n next() {\n this._state = this.rule.next(this.state);\n this._t += 1;\n }\n draw(p, cellSize) {\n const cellHorizontalRadius = cellSize / 2;\n const drawDiameter = cellSize * 0.9;\n const rowHeight = cellSize * (Math.sqrt(3) / 2);\n const cellVerticalRadius = rowHeight / 2;\n p.noStroke();\n p.fill(0xFF, 0xD0);\n for (let y = 0; y < this.state.length; y += 1) {\n const row = this.state[y];\n const isEvenRow = y % 2 === 0;\n for (let x = 0; x < row.length; x += 1) {\n const cell = row[x];\n if (cell !== 1) {\n continue;\n }\n const centerX = isEvenRow ? (x * cellSize) + cellHorizontalRadius : (x * cellSize);\n const centerY = (y * rowHeight) + cellVerticalRadius;\n p.circle(centerX, centerY, drawDiameter);\n }\n }\n }\n}\nfunction createState(size, type) {\n const localityAlivePoints = [size.randomized(), size.randomized(), size.randomized(), size.randomized()];\n const localityAliveDistance = Math.min(size.x, size.y) * 0.2;\n const centerX = Math.floor(size.x / 2);\n const centerY = Math.floor(size.y / 2);\n const state = [];\n for (let y = 0; y < size.y; y += 1) {\n const row = [];\n for (let x = 0; x < size.x; x += 1) {\n switch (type) {\n case \"random\":\n if ((0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < 0.5) {\n row.push(1);\n }\n else {\n row.push(0);\n }\n break;\n case \"one\":\n if (x === centerX && y === centerY) {\n row.push(1);\n }\n else {\n row.push(0);\n }\n break;\n case \"line\":\n if (y === centerY) {\n row.push(1);\n }\n else {\n row.push(0);\n }\n break;\n case \"gradation\": {\n const simpleGradationValue = Math.abs(y - centerY) / centerY;\n const gradationValue = Math.max(Math.min(simpleGradationValue * 1.2 - 0.1, 1), 0);\n if ((0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < gradationValue) {\n row.push(1);\n }\n else {\n row.push(0);\n }\n break;\n }\n case \"locality\": {\n const currentPoint = new _classes_physics__WEBPACK_IMPORTED_MODULE_1__.Vector(x, y);\n const nearestAlivePoint = localityAlivePoints.sort((lhs, rhs) => {\n const distanceL = currentPoint.dist(lhs);\n const distanceR = currentPoint.dist(rhs);\n if (distanceL === distanceR) {\n return 0;\n }\n return distanceL > distanceR ? 1 : -1;\n })[0];\n const distance = currentPoint.dist(nearestAlivePoint);\n if (distance > localityAliveDistance) {\n row.push(0);\n break;\n }\n if ((0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) > (distance / localityAliveDistance)) {\n row.push(1);\n }\n else {\n row.push(0);\n }\n break;\n }\n default:\n console.error(`Not implemented: initial state type ${type}`);\n break;\n }\n }\n state.push(row);\n }\n return state;\n}\n\n\n//# sourceURL=webpack://alife-lab/./src/simulations/hex_cellular_automata/model.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"Model\": () => /* binding */ Model\n/* harmony export */ });\n/* harmony import */ var _classes_utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../classes/utilities */ \"./src/classes/utilities.ts\");\n/* harmony import */ var _classes_physics__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../classes/physics */ \"./src/classes/physics.ts\");\n/* harmony import */ var _classes_color__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../classes/color */ \"./src/classes/color.ts\");\n\n\n\nclass Model {\n constructor(size, rule, initialState) {\n this.size = size;\n this.rule = rule;\n this._t = 0;\n this._state = createState(size, initialState);\n console.log(`rule: ${rule.rule}, size: ${size}`);\n }\n get t() {\n return this._t;\n }\n get state() {\n return this._state;\n }\n next() {\n this._state = this.rule.next(this.state);\n this._t += 1;\n }\n draw(p, cellSize) {\n var _a;\n const cellHorizontalRadius = cellSize / 2;\n const drawDiameter = cellSize * 0.9;\n const rowHeight = cellSize * (Math.sqrt(3) / 2);\n const cellVerticalRadius = rowHeight / 2;\n p.noStroke();\n for (let y = 0; y < this.state.length; y += 1) {\n const row = this.state[y];\n const isEvenRow = y % 2 === 0;\n for (let x = 0; x < row.length; x += 1) {\n const cell = row[x];\n if (cell.value !== 1) {\n continue;\n }\n const centerX = isEvenRow ? (x * cellSize) + cellHorizontalRadius : (x * cellSize);\n const centerY = (y * rowHeight) + cellVerticalRadius;\n p.fill(((_a = cell.color) !== null && _a !== void 0 ? _a : _classes_color__WEBPACK_IMPORTED_MODULE_2__.Color.white()).p5(p));\n p.circle(centerX, centerY, drawDiameter);\n }\n }\n }\n}\nfunction createState(size, type) {\n const localityAlivePoints = [size.randomized(), size.randomized(), size.randomized(), size.randomized()];\n const localityAliveDistance = Math.min(size.x, size.y) * 0.2;\n const centerX = Math.floor(size.x / 2);\n const centerY = Math.floor(size.y / 2);\n const state = [];\n const colors = [\n null,\n null,\n null,\n null,\n null,\n null,\n null,\n new _classes_color__WEBPACK_IMPORTED_MODULE_2__.Color(0xFC, 0x88, 0x45),\n new _classes_color__WEBPACK_IMPORTED_MODULE_2__.Color(83, 150, 205),\n ];\n const color = () => colors[Math.floor((0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(colors.length))];\n for (let y = 0; y < size.y; y += 1) {\n const row = [];\n for (let x = 0; x < size.x; x += 1) {\n switch (type) {\n case \"random\":\n if ((0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < 0.5) {\n row.push({ value: 1, color: color() });\n }\n else {\n row.push({ value: 0, color: null });\n }\n break;\n case \"one\":\n if (x === centerX && y === centerY) {\n row.push({ value: 1, color: color() });\n }\n else {\n row.push({ value: 0, color: null });\n }\n break;\n case \"line\":\n if (y === centerY) {\n row.push({ value: 1, color: color() });\n }\n else {\n row.push({ value: 0, color: null });\n }\n break;\n case \"gradation\": {\n const simpleGradationValue = Math.abs(y - centerY) / centerY;\n const gradationValue = Math.max(Math.min(simpleGradationValue * 1.2 - 0.1, 1), 0);\n if ((0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < gradationValue) {\n row.push({ value: 1, color: color() });\n }\n else {\n row.push({ value: 0, color: null });\n }\n break;\n }\n case \"locality\": {\n const currentPoint = new _classes_physics__WEBPACK_IMPORTED_MODULE_1__.Vector(x, y);\n const nearestAlivePoint = localityAlivePoints.sort((lhs, rhs) => {\n const distanceL = currentPoint.dist(lhs);\n const distanceR = currentPoint.dist(rhs);\n if (distanceL === distanceR) {\n return 0;\n }\n return distanceL > distanceR ? 1 : -1;\n })[0];\n const distance = currentPoint.dist(nearestAlivePoint);\n if (distance > localityAliveDistance) {\n row.push({ value: 0, color: null });\n break;\n }\n if ((0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) > (distance / localityAliveDistance)) {\n row.push({ value: 1, color: color() });\n }\n else {\n row.push({ value: 0, color: null });\n }\n break;\n }\n default:\n console.error(`Not implemented: initial state type ${type}`);\n break;\n }\n }\n state.push(row);\n }\n return state;\n}\n\n\n//# sourceURL=webpack://alife-lab/./src/simulations/hex_cellular_automata/model.ts?"); /***/ }), @@ -1521,7 +1532,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"BinaryRule\": () => /* binding */ BinaryRule\n/* harmony export */ });\n/* harmony import */ var _classes_utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../classes/utilities */ \"./src/classes/utilities.ts\");\n\n/*\n * ルール\n * a:2,3;s:3\n * a: alive\n * s: stay\n * radius = 1\n * state = [0, 1]\n */\nclass BinaryRule {\n constructor(rule) {\n this.rule = rule;\n this._radius = 1;\n const neighbours = this._radius * 6;\n const parseIntArray = (s) => {\n if (s.length === 0) {\n return [];\n }\n return s.split(\",\").map(str => {\n const num = parseInt(str, 10);\n if (isNaN(num)) {\n throw new Error(`Invalid rule: \"${str}\" is not a number (${rule})`);\n }\n if (num < 0 || num > neighbours) {\n throw new Error(`Invalid rule: invalid neighbour count: ${num} (${rule})`);\n }\n return num;\n });\n };\n const parseComponents = () => {\n const result = new Map();\n const components = rule.split(\";\");\n if (components.length !== 2) {\n throw new Error(`Invalid rule: ${rule}`);\n }\n components.forEach(component => {\n const pair = component.split(\":\");\n if (pair.length !== 2) {\n throw new Error(`Invalid rule: invalid format ${component} (${rule})`);\n }\n result.set(pair[0], parseIntArray(pair[1]));\n });\n return result;\n };\n const components = parseComponents();\n const alive = components.get(\"a\");\n if (alive == null) {\n throw new Error(`Invalid rule: missing alive component (${rule})`);\n }\n this._alive = alive;\n const stay = components.get(\"s\");\n if (stay == null) {\n throw new Error(`Invalid rule: missing stay component (${rule})`);\n }\n this._stay = stay;\n }\n get radius() {\n return this._radius;\n }\n get alive() {\n return this._alive;\n }\n get stay() {\n return this._stay;\n }\n static random() {\n const candidates = [0, 1, 2, 3, 4, 5, 6];\n const alive = candidates.filter(() => (0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < 0.5);\n const stay = candidates.filter(() => (0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < 0.5);\n const rule = `a:${alive.join(\",\")};s:${stay.join(\",\")}`;\n return new BinaryRule(rule);\n }\n next(map) {\n const result = [];\n for (let y = 0; y < map.length; y += 1) {\n const row = map[y];\n const resultRow = [];\n for (let x = 0; x < row.length; x += 1) {\n resultRow.push(this.nextBit(row[x], this.neighbourSum(map, x, y)));\n }\n result.push(resultRow);\n }\n return result;\n }\n nextBit(current, neighbours) {\n if (current === 0 && this.alive.includes(neighbours)) {\n return 1;\n }\n if (current === 1 && this.stay.includes(neighbours)) {\n return 1;\n }\n return 0;\n }\n // FixMe: この辺間違ってる: cellular_automaton を参照\n neighbourSum(map, x, y) {\n const radius = this.radius;\n let result = 0;\n const isEvenRow = y % 2 === 0;\n for (let j = -radius; j <= radius; j += 1) {\n const neighbourRow = map[(y + j + map.length) % map.length];\n for (let i = -radius; i <= radius; i += 1) {\n if (i === 0 && j === 0) {\n continue;\n }\n if (j !== 0) {\n if (isEvenRow && i < 0) {\n continue;\n }\n if (!isEvenRow && i > 0) {\n continue;\n }\n }\n const cell = neighbourRow[(x + i + neighbourRow.length) % neighbourRow.length];\n result += cell;\n }\n }\n return result;\n }\n}\n\n\n//# sourceURL=webpack://alife-lab/./src/simulations/hex_cellular_automata/rule.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"BinaryRule\": () => /* binding */ BinaryRule\n/* harmony export */ });\n/* harmony import */ var _classes_utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../classes/utilities */ \"./src/classes/utilities.ts\");\n/* harmony import */ var _classes_color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../classes/color */ \"./src/classes/color.ts\");\n\n\n/*\n * ルール\n * a:2,3;s:3\n * a: alive\n * s: stay\n * radius = 1\n * state = [0, 1]\n */\nclass BinaryRule {\n constructor(rule) {\n this.rule = rule;\n this._radius = 1;\n const neighbours = this._radius * 6;\n const parseIntArray = (s) => {\n if (s.length === 0) {\n return [];\n }\n return s.split(\",\").map(str => {\n const num = parseInt(str, 10);\n if (isNaN(num)) {\n throw new Error(`Invalid rule: \"${str}\" is not a number (${rule})`);\n }\n if (num < 0 || num > neighbours) {\n throw new Error(`Invalid rule: invalid neighbour count: ${num} (${rule})`);\n }\n return num;\n });\n };\n const parseComponents = () => {\n const result = new Map();\n const components = rule.split(\";\");\n if (components.length !== 2) {\n throw new Error(`Invalid rule: ${rule}`);\n }\n components.forEach(component => {\n const pair = component.split(\":\");\n if (pair.length !== 2) {\n throw new Error(`Invalid rule: invalid format ${component} (${rule})`);\n }\n result.set(pair[0], parseIntArray(pair[1]));\n });\n return result;\n };\n const components = parseComponents();\n const alive = components.get(\"a\");\n if (alive == null) {\n throw new Error(`Invalid rule: missing alive component (${rule})`);\n }\n this._alive = alive;\n const stay = components.get(\"s\");\n if (stay == null) {\n throw new Error(`Invalid rule: missing stay component (${rule})`);\n }\n this._stay = stay;\n }\n get radius() {\n return this._radius;\n }\n get alive() {\n return this._alive;\n }\n get stay() {\n return this._stay;\n }\n static random() {\n const candidates = [0, 1, 2, 3, 4, 5, 6];\n const alive = candidates.filter(() => (0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < 0.5);\n const stay = candidates.filter(() => (0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < 0.5);\n const rule = `a:${alive.join(\",\")};s:${stay.join(\",\")}`;\n return new BinaryRule(rule);\n }\n next(map) {\n const result = [];\n for (let y = 0; y < map.length; y += 1) {\n const row = map[y];\n const resultRow = [];\n for (let x = 0; x < row.length; x += 1) {\n const neighbours = this.neighbourSum(map, x, y);\n resultRow.push(this.nextBit(row[x], neighbours.count, neighbours.color));\n }\n result.push(resultRow);\n }\n return result;\n }\n nextBit(current, neighbours, color) {\n if (current.value === 0 && this.alive.includes(neighbours)) {\n return { value: 1, color: color !== null && color !== void 0 ? color : _classes_color__WEBPACK_IMPORTED_MODULE_1__.Color.white() };\n }\n if (current.value === 1 && this.stay.includes(neighbours)) {\n return current;\n }\n return { value: 0, color: null };\n }\n // FixMe: この辺間違ってる: cellular_automaton を参照\n neighbourSum(map, x, y) {\n const radius = this.radius;\n let result = 0;\n const isEvenRow = y % 2 === 0;\n let color = null;\n for (let j = -radius; j <= radius; j += 1) {\n const neighbourRow = map[(y + j + map.length) % map.length];\n for (let i = -radius; i <= radius; i += 1) {\n if (i === 0 && j === 0) {\n continue;\n }\n if (j !== 0) {\n if (isEvenRow && i < 0) {\n continue;\n }\n if (!isEvenRow && i > 0) {\n continue;\n }\n }\n const cell = neighbourRow[(x + i + neighbourRow.length) % neighbourRow.length];\n if (color == null && cell.value === 1) {\n color = cell.color;\n }\n result += cell.value;\n }\n }\n return { count: result, color: color };\n }\n}\n\n\n//# sourceURL=webpack://alife-lab/./src/simulations/hex_cellular_automata/rule.ts?"); /***/ }), diff --git a/dist/hex_cellular_automata_autosearch.js b/dist/hex_cellular_automata_autosearch.js index 00f3bc4..9e60490 100644 --- a/dist/hex_cellular_automata_autosearch.js +++ b/dist/hex_cellular_automata_autosearch.js @@ -1371,6 +1371,17 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ }), +/***/ "./src/classes/color.ts": +/*!******************************!*\ + !*** ./src/classes/color.ts ***! + \******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"Color\": () => /* binding */ Color\n/* harmony export */ });\nclass Color {\n constructor(r, g, b, alpha) {\n this.r = r;\n this.g = g;\n this.b = b;\n this.alpha = alpha !== null && alpha !== void 0 ? alpha : 0xFF;\n }\n static white(white, alpha) {\n return new Color(white !== null && white !== void 0 ? white : 0xFF, white !== null && white !== void 0 ? white : 0xFF, white !== null && white !== void 0 ? white : 0xFF, alpha !== null && alpha !== void 0 ? alpha : 0xFF);\n }\n p5(p, alpha) {\n return p.color(this.r, this.g, this.b, alpha !== null && alpha !== void 0 ? alpha : this.alpha);\n }\n toString() {\n const rawColorToString = (value) => {\n return value.toString(16)\n .padStart(2, \"0\");\n };\n return `#${rawColorToString(this.r)}${rawColorToString(this.g)}${rawColorToString(this.b)}`;\n }\n}\n\n\n//# sourceURL=webpack://alife-lab/./src/classes/color.ts?"); + +/***/ }), + /***/ "./src/classes/downloader.tsx": /*!************************************!*\ !*** ./src/classes/downloader.tsx ***! @@ -1499,7 +1510,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"Model\": () => /* binding */ Model\n/* harmony export */ });\n/* harmony import */ var _classes_utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../classes/utilities */ \"./src/classes/utilities.ts\");\n/* harmony import */ var _classes_physics__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../classes/physics */ \"./src/classes/physics.ts\");\n\n\nclass Model {\n constructor(size, rule, initialState) {\n this.size = size;\n this.rule = rule;\n this._t = 0;\n this._state = createState(size, initialState);\n console.log(`rule: ${rule.rule}, size: ${size}`);\n }\n get t() {\n return this._t;\n }\n get state() {\n return this._state;\n }\n next() {\n this._state = this.rule.next(this.state);\n this._t += 1;\n }\n draw(p, cellSize) {\n const cellHorizontalRadius = cellSize / 2;\n const drawDiameter = cellSize * 0.9;\n const rowHeight = cellSize * (Math.sqrt(3) / 2);\n const cellVerticalRadius = rowHeight / 2;\n p.noStroke();\n p.fill(0xFF, 0xD0);\n for (let y = 0; y < this.state.length; y += 1) {\n const row = this.state[y];\n const isEvenRow = y % 2 === 0;\n for (let x = 0; x < row.length; x += 1) {\n const cell = row[x];\n if (cell !== 1) {\n continue;\n }\n const centerX = isEvenRow ? (x * cellSize) + cellHorizontalRadius : (x * cellSize);\n const centerY = (y * rowHeight) + cellVerticalRadius;\n p.circle(centerX, centerY, drawDiameter);\n }\n }\n }\n}\nfunction createState(size, type) {\n const localityAlivePoints = [size.randomized(), size.randomized(), size.randomized(), size.randomized()];\n const localityAliveDistance = Math.min(size.x, size.y) * 0.2;\n const centerX = Math.floor(size.x / 2);\n const centerY = Math.floor(size.y / 2);\n const state = [];\n for (let y = 0; y < size.y; y += 1) {\n const row = [];\n for (let x = 0; x < size.x; x += 1) {\n switch (type) {\n case \"random\":\n if ((0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < 0.5) {\n row.push(1);\n }\n else {\n row.push(0);\n }\n break;\n case \"one\":\n if (x === centerX && y === centerY) {\n row.push(1);\n }\n else {\n row.push(0);\n }\n break;\n case \"line\":\n if (y === centerY) {\n row.push(1);\n }\n else {\n row.push(0);\n }\n break;\n case \"gradation\": {\n const simpleGradationValue = Math.abs(y - centerY) / centerY;\n const gradationValue = Math.max(Math.min(simpleGradationValue * 1.2 - 0.1, 1), 0);\n if ((0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < gradationValue) {\n row.push(1);\n }\n else {\n row.push(0);\n }\n break;\n }\n case \"locality\": {\n const currentPoint = new _classes_physics__WEBPACK_IMPORTED_MODULE_1__.Vector(x, y);\n const nearestAlivePoint = localityAlivePoints.sort((lhs, rhs) => {\n const distanceL = currentPoint.dist(lhs);\n const distanceR = currentPoint.dist(rhs);\n if (distanceL === distanceR) {\n return 0;\n }\n return distanceL > distanceR ? 1 : -1;\n })[0];\n const distance = currentPoint.dist(nearestAlivePoint);\n if (distance > localityAliveDistance) {\n row.push(0);\n break;\n }\n if ((0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) > (distance / localityAliveDistance)) {\n row.push(1);\n }\n else {\n row.push(0);\n }\n break;\n }\n default:\n console.error(`Not implemented: initial state type ${type}`);\n break;\n }\n }\n state.push(row);\n }\n return state;\n}\n\n\n//# sourceURL=webpack://alife-lab/./src/simulations/hex_cellular_automata/model.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"Model\": () => /* binding */ Model\n/* harmony export */ });\n/* harmony import */ var _classes_utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../classes/utilities */ \"./src/classes/utilities.ts\");\n/* harmony import */ var _classes_physics__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../classes/physics */ \"./src/classes/physics.ts\");\n/* harmony import */ var _classes_color__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../classes/color */ \"./src/classes/color.ts\");\n\n\n\nclass Model {\n constructor(size, rule, initialState) {\n this.size = size;\n this.rule = rule;\n this._t = 0;\n this._state = createState(size, initialState);\n console.log(`rule: ${rule.rule}, size: ${size}`);\n }\n get t() {\n return this._t;\n }\n get state() {\n return this._state;\n }\n next() {\n this._state = this.rule.next(this.state);\n this._t += 1;\n }\n draw(p, cellSize) {\n var _a;\n const cellHorizontalRadius = cellSize / 2;\n const drawDiameter = cellSize * 0.9;\n const rowHeight = cellSize * (Math.sqrt(3) / 2);\n const cellVerticalRadius = rowHeight / 2;\n p.noStroke();\n for (let y = 0; y < this.state.length; y += 1) {\n const row = this.state[y];\n const isEvenRow = y % 2 === 0;\n for (let x = 0; x < row.length; x += 1) {\n const cell = row[x];\n if (cell.value !== 1) {\n continue;\n }\n const centerX = isEvenRow ? (x * cellSize) + cellHorizontalRadius : (x * cellSize);\n const centerY = (y * rowHeight) + cellVerticalRadius;\n p.fill(((_a = cell.color) !== null && _a !== void 0 ? _a : _classes_color__WEBPACK_IMPORTED_MODULE_2__.Color.white()).p5(p));\n p.circle(centerX, centerY, drawDiameter);\n }\n }\n }\n}\nfunction createState(size, type) {\n const localityAlivePoints = [size.randomized(), size.randomized(), size.randomized(), size.randomized()];\n const localityAliveDistance = Math.min(size.x, size.y) * 0.2;\n const centerX = Math.floor(size.x / 2);\n const centerY = Math.floor(size.y / 2);\n const state = [];\n const colors = [\n null,\n null,\n null,\n null,\n null,\n null,\n null,\n new _classes_color__WEBPACK_IMPORTED_MODULE_2__.Color(0xFC, 0x88, 0x45),\n new _classes_color__WEBPACK_IMPORTED_MODULE_2__.Color(83, 150, 205),\n ];\n const color = () => colors[Math.floor((0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(colors.length))];\n for (let y = 0; y < size.y; y += 1) {\n const row = [];\n for (let x = 0; x < size.x; x += 1) {\n switch (type) {\n case \"random\":\n if ((0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < 0.5) {\n row.push({ value: 1, color: color() });\n }\n else {\n row.push({ value: 0, color: null });\n }\n break;\n case \"one\":\n if (x === centerX && y === centerY) {\n row.push({ value: 1, color: color() });\n }\n else {\n row.push({ value: 0, color: null });\n }\n break;\n case \"line\":\n if (y === centerY) {\n row.push({ value: 1, color: color() });\n }\n else {\n row.push({ value: 0, color: null });\n }\n break;\n case \"gradation\": {\n const simpleGradationValue = Math.abs(y - centerY) / centerY;\n const gradationValue = Math.max(Math.min(simpleGradationValue * 1.2 - 0.1, 1), 0);\n if ((0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < gradationValue) {\n row.push({ value: 1, color: color() });\n }\n else {\n row.push({ value: 0, color: null });\n }\n break;\n }\n case \"locality\": {\n const currentPoint = new _classes_physics__WEBPACK_IMPORTED_MODULE_1__.Vector(x, y);\n const nearestAlivePoint = localityAlivePoints.sort((lhs, rhs) => {\n const distanceL = currentPoint.dist(lhs);\n const distanceR = currentPoint.dist(rhs);\n if (distanceL === distanceR) {\n return 0;\n }\n return distanceL > distanceR ? 1 : -1;\n })[0];\n const distance = currentPoint.dist(nearestAlivePoint);\n if (distance > localityAliveDistance) {\n row.push({ value: 0, color: null });\n break;\n }\n if ((0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) > (distance / localityAliveDistance)) {\n row.push({ value: 1, color: color() });\n }\n else {\n row.push({ value: 0, color: null });\n }\n break;\n }\n default:\n console.error(`Not implemented: initial state type ${type}`);\n break;\n }\n }\n state.push(row);\n }\n return state;\n}\n\n\n//# sourceURL=webpack://alife-lab/./src/simulations/hex_cellular_automata/model.ts?"); /***/ }), @@ -1510,7 +1521,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"BinaryRule\": () => /* binding */ BinaryRule\n/* harmony export */ });\n/* harmony import */ var _classes_utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../classes/utilities */ \"./src/classes/utilities.ts\");\n\n/*\n * ルール\n * a:2,3;s:3\n * a: alive\n * s: stay\n * radius = 1\n * state = [0, 1]\n */\nclass BinaryRule {\n constructor(rule) {\n this.rule = rule;\n this._radius = 1;\n const neighbours = this._radius * 6;\n const parseIntArray = (s) => {\n if (s.length === 0) {\n return [];\n }\n return s.split(\",\").map(str => {\n const num = parseInt(str, 10);\n if (isNaN(num)) {\n throw new Error(`Invalid rule: \"${str}\" is not a number (${rule})`);\n }\n if (num < 0 || num > neighbours) {\n throw new Error(`Invalid rule: invalid neighbour count: ${num} (${rule})`);\n }\n return num;\n });\n };\n const parseComponents = () => {\n const result = new Map();\n const components = rule.split(\";\");\n if (components.length !== 2) {\n throw new Error(`Invalid rule: ${rule}`);\n }\n components.forEach(component => {\n const pair = component.split(\":\");\n if (pair.length !== 2) {\n throw new Error(`Invalid rule: invalid format ${component} (${rule})`);\n }\n result.set(pair[0], parseIntArray(pair[1]));\n });\n return result;\n };\n const components = parseComponents();\n const alive = components.get(\"a\");\n if (alive == null) {\n throw new Error(`Invalid rule: missing alive component (${rule})`);\n }\n this._alive = alive;\n const stay = components.get(\"s\");\n if (stay == null) {\n throw new Error(`Invalid rule: missing stay component (${rule})`);\n }\n this._stay = stay;\n }\n get radius() {\n return this._radius;\n }\n get alive() {\n return this._alive;\n }\n get stay() {\n return this._stay;\n }\n static random() {\n const candidates = [0, 1, 2, 3, 4, 5, 6];\n const alive = candidates.filter(() => (0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < 0.5);\n const stay = candidates.filter(() => (0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < 0.5);\n const rule = `a:${alive.join(\",\")};s:${stay.join(\",\")}`;\n return new BinaryRule(rule);\n }\n next(map) {\n const result = [];\n for (let y = 0; y < map.length; y += 1) {\n const row = map[y];\n const resultRow = [];\n for (let x = 0; x < row.length; x += 1) {\n resultRow.push(this.nextBit(row[x], this.neighbourSum(map, x, y)));\n }\n result.push(resultRow);\n }\n return result;\n }\n nextBit(current, neighbours) {\n if (current === 0 && this.alive.includes(neighbours)) {\n return 1;\n }\n if (current === 1 && this.stay.includes(neighbours)) {\n return 1;\n }\n return 0;\n }\n // FixMe: この辺間違ってる: cellular_automaton を参照\n neighbourSum(map, x, y) {\n const radius = this.radius;\n let result = 0;\n const isEvenRow = y % 2 === 0;\n for (let j = -radius; j <= radius; j += 1) {\n const neighbourRow = map[(y + j + map.length) % map.length];\n for (let i = -radius; i <= radius; i += 1) {\n if (i === 0 && j === 0) {\n continue;\n }\n if (j !== 0) {\n if (isEvenRow && i < 0) {\n continue;\n }\n if (!isEvenRow && i > 0) {\n continue;\n }\n }\n const cell = neighbourRow[(x + i + neighbourRow.length) % neighbourRow.length];\n result += cell;\n }\n }\n return result;\n }\n}\n\n\n//# sourceURL=webpack://alife-lab/./src/simulations/hex_cellular_automata/rule.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"BinaryRule\": () => /* binding */ BinaryRule\n/* harmony export */ });\n/* harmony import */ var _classes_utilities__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../classes/utilities */ \"./src/classes/utilities.ts\");\n/* harmony import */ var _classes_color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../classes/color */ \"./src/classes/color.ts\");\n\n\n/*\n * ルール\n * a:2,3;s:3\n * a: alive\n * s: stay\n * radius = 1\n * state = [0, 1]\n */\nclass BinaryRule {\n constructor(rule) {\n this.rule = rule;\n this._radius = 1;\n const neighbours = this._radius * 6;\n const parseIntArray = (s) => {\n if (s.length === 0) {\n return [];\n }\n return s.split(\",\").map(str => {\n const num = parseInt(str, 10);\n if (isNaN(num)) {\n throw new Error(`Invalid rule: \"${str}\" is not a number (${rule})`);\n }\n if (num < 0 || num > neighbours) {\n throw new Error(`Invalid rule: invalid neighbour count: ${num} (${rule})`);\n }\n return num;\n });\n };\n const parseComponents = () => {\n const result = new Map();\n const components = rule.split(\";\");\n if (components.length !== 2) {\n throw new Error(`Invalid rule: ${rule}`);\n }\n components.forEach(component => {\n const pair = component.split(\":\");\n if (pair.length !== 2) {\n throw new Error(`Invalid rule: invalid format ${component} (${rule})`);\n }\n result.set(pair[0], parseIntArray(pair[1]));\n });\n return result;\n };\n const components = parseComponents();\n const alive = components.get(\"a\");\n if (alive == null) {\n throw new Error(`Invalid rule: missing alive component (${rule})`);\n }\n this._alive = alive;\n const stay = components.get(\"s\");\n if (stay == null) {\n throw new Error(`Invalid rule: missing stay component (${rule})`);\n }\n this._stay = stay;\n }\n get radius() {\n return this._radius;\n }\n get alive() {\n return this._alive;\n }\n get stay() {\n return this._stay;\n }\n static random() {\n const candidates = [0, 1, 2, 3, 4, 5, 6];\n const alive = candidates.filter(() => (0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < 0.5);\n const stay = candidates.filter(() => (0,_classes_utilities__WEBPACK_IMPORTED_MODULE_0__.random)(1) < 0.5);\n const rule = `a:${alive.join(\",\")};s:${stay.join(\",\")}`;\n return new BinaryRule(rule);\n }\n next(map) {\n const result = [];\n for (let y = 0; y < map.length; y += 1) {\n const row = map[y];\n const resultRow = [];\n for (let x = 0; x < row.length; x += 1) {\n const neighbours = this.neighbourSum(map, x, y);\n resultRow.push(this.nextBit(row[x], neighbours.count, neighbours.color));\n }\n result.push(resultRow);\n }\n return result;\n }\n nextBit(current, neighbours, color) {\n if (current.value === 0 && this.alive.includes(neighbours)) {\n return { value: 1, color: color !== null && color !== void 0 ? color : _classes_color__WEBPACK_IMPORTED_MODULE_1__.Color.white() };\n }\n if (current.value === 1 && this.stay.includes(neighbours)) {\n return current;\n }\n return { value: 0, color: null };\n }\n // FixMe: この辺間違ってる: cellular_automaton を参照\n neighbourSum(map, x, y) {\n const radius = this.radius;\n let result = 0;\n const isEvenRow = y % 2 === 0;\n let color = null;\n for (let j = -radius; j <= radius; j += 1) {\n const neighbourRow = map[(y + j + map.length) % map.length];\n for (let i = -radius; i <= radius; i += 1) {\n if (i === 0 && j === 0) {\n continue;\n }\n if (j !== 0) {\n if (isEvenRow && i < 0) {\n continue;\n }\n if (!isEvenRow && i > 0) {\n continue;\n }\n }\n const cell = neighbourRow[(x + i + neighbourRow.length) % neighbourRow.length];\n if (color == null && cell.value === 1) {\n color = cell.color;\n }\n result += cell.value;\n }\n }\n return { count: result, color: color };\n }\n}\n\n\n//# sourceURL=webpack://alife-lab/./src/simulations/hex_cellular_automata/rule.ts?"); /***/ }), diff --git a/package.json b/package.json index e00f0cb..4416aac 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "react-dom": "^17.0.1" }, "scripts": { - "build": "webpack && python src/html-generator/generate.py", + "build": "webpack && python src/html-generator/generate.py && echo '\\007'", "watch": "webpack -w", "test": "jest", "start": "webpack serve --open --mode development" diff --git a/src/simulations/hex_cellular_automata/model.ts b/src/simulations/hex_cellular_automata/model.ts index c499999..654e63f 100644 --- a/src/simulations/hex_cellular_automata/model.ts +++ b/src/simulations/hex_cellular_automata/model.ts @@ -1,6 +1,7 @@ import p5 from "p5" import { random } from "../../classes/utilities" import { Vector } from "../../classes/physics" +import { Color } from "../../classes/color" import { InitialStateType } from "./initial_state_type" import { BinaryRule, State, Bit } from "./rule" @@ -36,19 +37,19 @@ export class Model { const cellVerticalRadius = rowHeight / 2 p.noStroke() - p.fill(0xFF, 0xD0) for (let y = 0; y < this.state.length; y += 1) { const row = this.state[y] const isEvenRow = y % 2 === 0 for (let x = 0; x < row.length; x += 1) { const cell = row[x] - if (cell !== 1) { + if (cell.value !== 1) { continue } const centerX = isEvenRow ? (x * cellSize) + cellHorizontalRadius : (x * cellSize) const centerY = (y * rowHeight) + cellVerticalRadius + p.fill((cell.color ?? Color.white()).p5(p)) p.circle(centerX, centerY, drawDiameter) } } @@ -61,31 +62,44 @@ function createState(size: Vector, type: InitialStateType): State { const centerX = Math.floor(size.x / 2) const centerY = Math.floor(size.y / 2) const state: State = [] + const colors: (Color | null)[] = [ + null, + null, + null, + null, + null, + null, + null, + new Color(0xFC, 0x88, 0x45), + new Color(83, 150, 205), + ] + const color = (): Color | null => colors[Math.floor(random(colors.length))] + for (let y = 0; y < size.y; y += 1) { const row: Bit[] = [] for (let x = 0; x < size.x; x += 1) { switch (type) { case "random": if (random(1) < 0.5) { - row.push(1) + row.push({ value: 1, color: color() }) } else { - row.push(0) + row.push({ value: 0, color: null }) } break case "one": if (x === centerX && y === centerY) { - row.push(1) + row.push({ value: 1, color: color() }) } else { - row.push(0) + row.push({ value: 0, color: null }) } break case "line": if (y === centerY) { - row.push(1) + row.push({ value: 1, color: color() }) } else { - row.push(0) + row.push({ value: 0, color: null }) } break @@ -93,9 +107,9 @@ function createState(size: Vector, type: InitialStateType): State { const simpleGradationValue = Math.abs(y - centerY) / centerY const gradationValue = Math.max(Math.min(simpleGradationValue * 1.2 - 0.1, 1), 0) if (random(1) < gradationValue) { - row.push(1) + row.push({ value: 1, color: color() }) } else { - row.push(0) + row.push({ value: 0, color: null }) } break } @@ -113,13 +127,13 @@ function createState(size: Vector, type: InitialStateType): State { const distance = currentPoint.dist(nearestAlivePoint) if (distance > localityAliveDistance) { - row.push(0) + row.push({ value: 0, color: null }) break } if (random(1) > (distance / localityAliveDistance)) { - row.push(1) + row.push({ value: 1, color: color() }) } else { - row.push(0) + row.push({ value: 0, color: null }) } break } diff --git a/src/simulations/hex_cellular_automata/rule.ts b/src/simulations/hex_cellular_automata/rule.ts index ea05a0d..3de0a7a 100644 --- a/src/simulations/hex_cellular_automata/rule.ts +++ b/src/simulations/hex_cellular_automata/rule.ts @@ -1,8 +1,17 @@ import { random } from "../../classes/utilities" +import { Color } from "../../classes/color" -export type Bit = 0 | 1 +export interface Bit { + value: 0 | 1 + color: Color | null +} export type State = Bit[][] +interface Neighbours { + count: number + color: Color | null +} + /* * ルール * a:2,3;s:3 @@ -86,28 +95,30 @@ export class BinaryRule { const row = map[y] const resultRow: Bit[] = [] for (let x = 0; x < row.length; x += 1) { - resultRow.push(this.nextBit(row[x], this.neighbourSum(map, x, y))) + const neighbours = this.neighbourSum(map, x, y) + resultRow.push(this.nextBit(row[x], neighbours.count, neighbours.color)) } result.push(resultRow) } return result } - public nextBit(current: Bit, neighbours: number): Bit { - if (current === 0 && this.alive.includes(neighbours)) { - return 1 + public nextBit(current: Bit, neighbours: number, color: Color | null): Bit { + if (current.value === 0 && this.alive.includes(neighbours)) { + return { value: 1, color: color ?? Color.white() } } - if (current === 1 && this.stay.includes(neighbours)) { - return 1 + if (current.value === 1 && this.stay.includes(neighbours)) { + return current } - return 0 + return { value: 0, color: null } } // FixMe: この辺間違ってる: cellular_automaton を参照 - public neighbourSum(map: State, x: number, y: number): number { + public neighbourSum(map: State, x: number, y: number): Neighbours { const radius = this.radius let result = 0 const isEvenRow = y % 2 === 0 + let color: Color | null = null for (let j = -radius; j <= radius; j += 1) { const neighbourRow = map[(y + j + map.length) % map.length] for (let i = -radius; i <= radius; i += 1) { @@ -123,9 +134,12 @@ export class BinaryRule { } } const cell = neighbourRow[(x + i + neighbourRow.length) % neighbourRow.length] - result += cell + if (color == null && cell.value === 1) { + color = cell.color + } + result += cell.value } } - return result + return { count: result, color: color } } } \ No newline at end of file