Песочница →
Пишем класс на php для интерпретации BrainLoller
Пример Brainloller кода
BrainLoller — это визуальный диалект языка Brainfuck, в котором символьные команды заменяются на пиксели разных цветов, считываемые с изображения в формате *.png.
Brainloller был придуман в 2005 году Lode Vandevenne.
Цвета команд были выбраны как комбинации значений 0xFF и 0x80 для трех RGB компонент. В результате была получена следующая таблица команд:
- + = 0x00FF00
- — = 0x008000
- > = 0xFF0000
- < = 0x800000
- [ = 0xFFFF00
- ] = 0x808000
- . = 0x0000FF
- , = 0x000080
В Brainloller добавлены также две дополнительные команды — 0x00FFFF (поворот указателя инструкций против часовой стрелки) и 0x008080 (поворот по часовой). Таким образом, “код” может записываться не в одну строку пикселей, а в двухмерное изображение. Перед началом выполения программы указатель инструкций находится в верхнем левом пикселе и направлен вправо. Изображение обрабатывается попиксельно, после каждой команды Brainfuck указательно инструкций сдвигается на один пиксель в том направлении, в котором он указывает. Выполнение программы заканчивается, как только указатель инструкций выходит за пределы изображения. (источник)
К сожалению, исходный набор инструментов для работы с Brainloller был утерян и мы попробуем в этой статье восполнить пробел)
Пишем класс
Для создания класса нам нужно:
- тестовый полигон который может исполнять php скрипты (веб-хост, локальный веб сервер)
- библиотека GD на тестовом полигоне
- собственно тестовое изображение ( сохранить как hello-world.png)
Теперь нам нужен какой либо класс для обработки Brainfuck на php (незачем городить велосипеды, наш класс просто будет превращать пиксели в набор обычных Brainfuck команд (не будем рассматривать вариант, когда на изображении идет бесконечный цикл)), к примеру я заюзал вот этот и сохранил как brainfuck.class.php. Этот класс выдавал warning, поэтому рекомендую на 79 строчке поставить подавление ошибок @
Можно приступить к кодингу. Нам нужно создать brainloller.class.php и index.php. Первый файл будет тем классом который мы пишем, второй же будет запускать его с конкретным изображением.
Теперь приступим к описанию класса.
Открываем brainloller.class.php и добавляем
<?php
class BrainLoller {
protected $picture; // Картинка с кодом
function __construct($picture){
// Открываем изображение
$this->picture = imagecreatefrompng($picture);
}
function __destruct(){
imagedestroy($this->picture); // Высвобождаем занятые ресурсы
}
}
Мы создаем класс с конструктором и деструктором в котором инициализируем GD картинку. Картинка будет храниться в $picture.
Теперь нам нужна функция которая будет проходить по изображению получая код, назовем ее например getCode().
После добавления этой функции класс будет выглядеть так
<?php
define('DIRECTION_UP', 0);
define('DIRECTION_RIGHT', 1);
define('DIRECTION_DOWN', 2);
define('DIRECTION_LEFT', 3);
/*
Часы
-
- 0 -
-
*/
class BrainLoller {
protected $picture; // Картинка BrainLoller
protected $pointer = DIRECTION_RIGHT; // Указатель куда идти на следующем шаге (0 = 'up', 1 = 'right', 2 = 'down', 3 = 'left')
function __construct($picture){
// Открываем собственно изображение
$this->picture = imagecreatefrompng($picture);
// Получаем размер изображения
$size = getimagesize($picture);
$this->size['w'] = $size[0];
$this->size['h'] = $size[1];
}
public function getCode(){
$current_pixel = array(0, 0); // Текущий пиксель (x, y)
$this->pointer = DIRECTION_RIGHT;
$code = '';
for(;;){
if($current_pixel[0] > $this->size['w'] or $current_pixel[1] > $this->size['h'] or $current_pixel[0] < 0 or $current_pixel[1] < 0) break; // Если вышли за пределы то прерываем цикл
$pixel_color = imagecolorat($this->picture, $current_pixel[0], $current_pixel[1]); // Берем цвет пикселя
$pixel_color = array(
( ($pixel_color >> 16) & 0xFF ), // Red
( ($pixel_color >> 8) & 0xFF ), // Green
( $pixel_color & 0xFF ), // Blue
);
// Проверяем какой команде сопоставлен данный цвет, если поворот то поворачиваем, если команда то добавляем к $code
switch($pixel_color){
case array(0, 255, 0):
// +
$code .= '+';
break;
case array(0, 128, 0):
// -
$code .= '-';
break;
case array(255, 0, 0):
// >
$code .= '>';
break;
case array(128, 0, 0):
// <
$code .= '<';
break;
case array(255, 255, 0):
// [
$code .= '[';
break;
case array(128, 128, 0):
// ]
$code .= ']';
break;
case array(0, 0, 255):
// .
$code .= '.';
break;
case array(0, 0, 128):
// ,
$code .= ',';
break;
case array(0, 255, 255):
// <-
if($this->pointer + 1 > DIRECTION_LEFT) $this->pointer = 0;
else $this->pointer += 1;
break;
case array(0, 128, 128):
// ->
if($this->pointer - 1 < DIRECTION_UP) $this->pointer = 3;
else $this->pointer -= 1;
break;
}
switch($this->pointer){
case DIRECTION_UP:
$current_pixel[1] -= 1;
break;
case DIRECTION_RIGHT:
$current_pixel[0] += 1;
break;
case DIRECTION_DOWN:
$current_pixel[1] += 1;
break;
case DIRECTION_LEFT:
$current_pixel[0] -= 1;
break;
}
}
return $code;
}
function __destruct(){
imagedestroy($this->picture); // Высвобождаем занятые ресурсы
}
}
В функции есть бесконечный цикл который обрабатывает каждый пиксель.
Сначала проверяем не вышли ли мы за пределы изображения, потому что по условию выход изображения это конец программы.
Затем получаем цвет пикселя в формате RGB, и проверяем к какой команде он прикреплен.
Если цвет прикреплен к команде brainfuck кода, то добавляем к конечному листингу-кода эту команду, если же цвет является командой поворота, то поворачиваем указатель.
Затем глядя на то в какую сторону смотрит указатель — смещаем адрес пикселя (двигаемся дальше в сторону взгляда указателя).
Когда цикл завершается — значит мы вышли за пределы изображения и надо вернуть полученный код для дальнейшей интерпретацией другим классом.
Создадим обработчик в index.php
<?php
require 'brainloller.class.php';
require 'brainfuck.class.php';
$brainloller = new BrainLoller('hello-world.png');
$brainfuck = new Brainfuck($brainloller->getCode(), 1);
$brainfuck->run();
Здесь мы загружаем оба класса, и создаем их экземпляры. После чего получаем brainfuck код из нашего класса и передаем на интерпретацию в другой класс, от стороннего автора (Опять же, рекомендую как минимум не изобретать велосипеды, как максимум улучшать уже существующие решения если они написаны грамотно и автор идет на контакт)
Теперь можно полюбоваться на наше творение в браузере
Финальный код можно скачать отсюда (зеркало)
P. S.
С чем можно поиграться еще?
- Заюзать новую фишку php 5.3 сделав статический метод который будет возвращать код без создания класса (например BrainLoller::getCode('picture_url'))
- Сделать порог для цикла, чтобы он не длился бесконечно (на некоторых изображениях из которых никогда нельзя выйти (нужно добавить к for ограничение) )
- Сделать класс для создания BrainLoller изображений (например определить символ который будет разворачивать указатель строки по часовой/Против часовой стрелки, и таким образом с небольшой переделкой brainfuck кода можно будет напрямую превращать код в картинку и наоборот)
- Оптимизировать этот код (Наверняка можно было написать компактнее)
16.09.2011 05:04+0400