ラングトンのアリの描画
Posted feedbacks - Flatten
Nested HiddenC++でWTL使いました。GDIで描画、Windowsのタイマーメッセージ (WM_TIMER) を使って回数を進めるようにしています。高速に結果を見たかったので、1度のタイマイベントで複数回進めるようにしています。
ありを進める部分はなんの工夫もないですが。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | #define WINVER 0x0400
#define _WIN32_WINDOWS 0
#define _WIN32_WINNT 0
#define _WIN32_IE 0x0300 // IE3以上を指定しないとWTLがコンパイルエラーを起こす。
#define WIN32_LEAN_AND_MEAN
#define _ATL_NO_AUTOMATIC_NAMESPACE
#define _WTL_NO_AUTOMATIC_NAMESPACE
#include <cassert>
#include <sstream>
#include <tchar.h> // _tWinMain
#include <windows.h>
#include <atlbase.h> // ATL共通ヘッダ
#include <atlwin.h> // CWindowImplほか
#include <atlcrack.h> // MSG_*
#include <atlapp.h> // <atlmisc.h>
#include <atlmisc.h> // CPoint
const int ID_TIMER = 1;
enum Direction {Up, Right, Down, Left};
class MainWindow :
public ATL::CWindowImpl<MainWindow, ATL::CWindow, ATL::CFrameWinTraits>
{
public:
MainWindow() : field(), x(100), y(100), direction(Up), step() {} // このxとyの値が初期位置
// ウィンドウクラス名を登録
DECLARE_WND_CLASS(TEXT("Langton's ant"));
// メッセージマップ
BEGIN_MSG_MAP(MainWindow)
MSG_WM_TIMER(OnTimer)
MSG_WM_PAINT(OnPaint)
MSG_WM_CREATE(OnCreate)
MSG_WM_DESTROY(OnDestroy)
END_MSG_MAP()
private:
void OnTimer(UINT /*idEvent*/)
{
for (int i = 0; i < 16; ++i) // 1度のタイマイベントで複数回進める。
{
NextStep();
++step;
}
std::basic_ostringstream<TCHAR> oss;
oss << TEXT("Langton's ant - step: ") << step;
SetWindowText(oss.str().c_str());
Invalidate(FALSE);
}
static const int TILE_WIDTH = 2, TILE_HEIGHT = 2;
static const int X = 200, Y = 200;
void OnPaint(HDC)
{
WTL::CPaintDC dc(m_hWnd);
for (int i = 0; i < Y; ++i)
{
for (int j = 0; j < X; ++j)
{
dc.FillSolidRect(j * TILE_WIDTH, i * TILE_HEIGHT,
TILE_WIDTH, TILE_HEIGHT,
field[i][j] ? RGB(255, 255, 255)
: RGB(0, 0, 0));
}
}
}
LRESULT OnCreate(CREATESTRUCT const* pcs)
{
RECT rc = {0, 0, TILE_WIDTH * X, TILE_HEIGHT * Y};
AdjustWindowRectEx(&rc, pcs->style, FALSE, pcs->dwExStyle);
SetWindowPos(0, &rc, SWP_NOMOVE | SWP_NOZORDER);
SetTimer(ID_TIMER, 10, 0);
return 0;
}
void OnDestroy()
{
PostQuitMessage(0);
}
void NextStep()
{
if (field[y][x]) // trueが白、falseが黒としている
{
if (direction == Up)
{
direction = Left;
}
else
{
--direction;
}
}
else
{
if (direction == Left)
{
direction = Up;
}
else
{
++direction;
}
}
field[y][x] = !field[y][x];
switch (direction)
{
case Up:
y--;
if (y == -1)
{
y = Y - 1;
}
break;
case Right:
x++;
if (x == X)
{
x = 0;
}
break;
case Down:
y++;
if (y == Y)
{
y = 0;
}
break;
case Left:
x--;
if (x == -1)
{
x = X - 1;
}
break;
default:
assert(0);
}
}
bool field[Y][X];
int y;
int x;
int direction; //Direction(列挙)型では++と--できない(できるようにするのが面倒)。
int step;
};
int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int cmdShow)
{
MainWindow wnd;
wnd.Create(NULL, ATL::CWindow::rcDefault, TEXT("Langton's ant"),
WS_CAPTION | WS_SYSMENU | WS_DLGFRAME | WS_MINIMIZEBOX
| WS_OVERLAPPED | WS_SYSMENU, WS_EX_APPWINDOW);
wnd.ShowWindow(cmdShow);
wnd.UpdateWindow();
WTL::CMessageLoop msgLoop;
return msgLoop.Run();
}
|
C#でビットマップにごりごり描画します。 フレームスキップして速度を稼いでいます。
今回一番はまったのは、Color.FromArgb(255, 0, 0, 0) が Color.Black とイコールではないってこと。ColorはARGB以外の情報を持っているので、ToArgb()で比較するように、とMSDNにある。
see: Color.Equality 演算子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace LangtonsAnt {
class Ant {
Point pos;
int direction;
public int X { get { return pos.X; } }
public int Y { get { return pos.Y; } }
public Ant(int x, int y) {
pos.X = x;
pos.Y = y;
direction = 0;
}
public void Advance(bool IsBlack) {
if (IsBlack) {
direction = (direction + 1) % 4;
} else {
direction = (direction + 3) % 4;
}
switch (direction) {
case 0: pos.Y--; break; //北
case 1: pos.X++; break; //東
case 2: pos.Y++; break; //南
case 3: pos.X--; break; //西
}
}
}
class Form1 : Form {
Bitmap bitmap; //世界=ビットマップ
List<Ant> ants; //アリたち
Timer timer; //タイマー
long step_count; //ステップ数
Color Black = Color.FromArgb(255, 0, 0, 0);
Color White = Color.FromArgb(255, 255, 255, 255);
public Form1() {
//マップの初期化
this.Width = 300; this.Height = 200;
bitmap = new Bitmap(300, 200, Graphics.FromHwnd(this.Handle));
//100,100を中心に黒点を打つ
Random r = new Random();
for (int i = 0 ; i < 100 ; i++) {
int x = r.Next(40) + bitmap.Width / 2 - 20;
int y = r.Next(40) + bitmap.Height / 2 - 20;
bitmap.SetPixel(x, y, Black);
}
//アリの初期化
ants = new List<Ant>();
ants.Add(new Ant(r.Next(20) + bitmap.Width / 2 - 10, r.Next(20) + bitmap.Height / 2 - 10));
ants.Add(new Ant(r.Next(20) + bitmap.Width / 2 - 10, r.Next(20) + bitmap.Height / 2 - 10));
ants.Add(new Ant(r.Next(20) + bitmap.Width / 2 - 10, r.Next(20) + bitmap.Height / 2 - 10));
//タイマー初期化
step_count = 0;
timer = new Timer();
timer.Interval = 300; //300msに1度再描画
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
void timer_Tick(object sender, EventArgs e) {
//高速化のため1描画につき150周回す
for (int step = 0 ; step < 150 ; step++) {
foreach (Ant ant in ants) {
int x = (ant.X + bitmap.Width) % bitmap.Width;
if (x < 0) x += bitmap.Width;
int y = (ant.Y + bitmap.Height) % bitmap.Height;
if (y < 0) y += bitmap.Height;
Color color = bitmap.GetPixel(x, y);
if (color == Black) {
ant.Advance(true);
bitmap.SetPixel(x, y, White);
} else {
ant.Advance(false);
bitmap.SetPixel(x, y, Black);
}
}
}
Refresh();
step_count += 150;
Text = step_count.ToString();
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
e.Graphics.DrawImage(bitmap, ClientRectangle);
}
protected override void OnClosed(EventArgs e) {
timer.Stop(); timer = null;
base.OnClosed(e);
}
}
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
|
ブラウザ向けに結果を出力します。 パラメータはフィールドの「幅」と「高さ」で、以下のように使用します。 (アリはフィールドの中心に落ちます) lang_ant(300, 300);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <?php
function lang_ant($w, $h)
{
$world = array_fill(0, $h, array_fill(0, $w, 1));
$x = (int)($w/2);
$y = (int)($h/2);
$dct = 0; /* 0:N 1:E 2:S 3:W */
$stp_ant = array('$x++;', '$y++;', '$x--;', '$y--;');
$chg_dct = array('$dct = (((++$dct)%4)+2)%4;', '$dct = (++$dct)%4;');
$print_world = array('0', ' ');
for ( $i = 0; $i < 20000; $i++) {
eval($chg_dct[$world[$y][$x]]);
eval($stp_ant[$dct]);
if ($x<0 || $w<=$x || $y<0 || $h<=$y) {
break;
}
$world[$y][$x] = (++$world[$y][$x])%2;
}
foreach ( $world as $cels) {
foreach ($cels as $cel) {
echo $print_world[$cel];
}
echo "<br />";
}
}
?>
|
Squeak Smalltalk で。
1 2 3 4 5 6 7 8 9 10 11 12 | Display fillWhite; restoreAfter: [
| pen black white |
pen := Pen new.
black := pen color: Color black; color.
white := Color white.
[Sensor leftShiftDown] whileFalse: [
(Display colorAt: pen location) caseOf: {
[black] -> [pen color: white; turn: -90].
[white] -> [pen color: black; turn: 90]} otherwise: [].
pen down; go: 0; up; go: 1.
pen instVarNamed: #direction put: pen direction \\ 360]
]
|
JPanelに直接描画してみました。 今時のJVMだと結構早い描画ができると思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Sample276 extends JFrame {
private final LangtonsAntPanel mainPanel_;
private final JButton startButton_ = new JButton(new AbstractAction("start") {
public void actionPerformed(ActionEvent e) {
timer_.start();
changeMode(true);
}
});
private final JButton stopButton_ = new JButton(new AbstractAction("stop") {
public void actionPerformed(ActionEvent e) {
timer_.stop();
changeMode(false);
}
});
private final JButton resetButton_ = new JButton(new AbstractAction("reset") {
public void actionPerformed(ActionEvent e) {
timer_.stop();
changeMode(false);
mainPanel_.reset();
mainPanel_.repaint();
}
});
private final JButton endButton_ = new JButton(new AbstractAction("end") {
public void actionPerformed(ActionEvent e) {
Sample276.this.dispose();
}
});
private final Timer timer_;
public Sample276(int size) {
super("Langton's ant.");
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
mainPanel_ = new LangtonsAntPanel(size);
JPanel panel = new JPanel(new BorderLayout(3, 3));
panel.add(mainPanel_, BorderLayout.CENTER);
panel.add(createButtonPanel(), BorderLayout.SOUTH);
this.add(panel);
this.pack();
timer_ = new Timer(5, new ActionListener() {
public void actionPerformed(ActionEvent e) {
boolean res = mainPanel_.tick();
mainPanel_.repaint();
if (!res) {
timer_.stop();
changeMode(false);
JOptionPane.showMessageDialog(Sample276.this, "Langton's ant is dead.");
}
}
});
}
private JComponent createButtonPanel() {
JPanel panel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 3, 3));
panel.add(startButton_);
panel.add(stopButton_);
panel.add(resetButton_);
panel.add(endButton_);
return panel;
}
private void changeMode(boolean isMoving) {
startButton_.setEnabled(!isMoving);
stopButton_.setEnabled(isMoving);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(Exception e) {
throw new InternalError(e.toString());
}
Sample276 frame = new Sample276(100);
frame.setVisible(true);
};
});
}
}
class LangtonsAntPanel extends JPanel {
public enum Direction {
N(0, -1),
E(1, 0),
S(0, 1),
W(-1, 0);
public final int dx;
public final int dy;
private Direction(int x, int y) {
dx = x;
dy = y;
}
public Direction turn(boolean isRight) {
int dd = this.ordinal() + (isRight? 1: 3);
return Direction.values()[dd % 4];
}
}
public class Ant {
private int x_;
private int y_;
private Direction d_ = Direction.N;
private int counter_ = 0;
public Ant(int x, int y) {
x_ = x;
y_ = y;
}
public int getX() {
return x_;
}
public int getY() {
return y_;
}
public int getCounter() {
return counter_;
}
public void next(boolean isBlack) {
d_ = d_.turn(isBlack);
x_ += d_.dx;
y_ += d_.dy;
counter_++;
}
}
private static final int ANT_SIZE = 3;
private final int size_;
private final boolean[][] land_;
private Ant ant_;
public LangtonsAntPanel(int size) {
super(true);
size_ = size;
land_ = new boolean[size][];
for (int index = 0; index < land_.length; index++) {
land_[index] = new boolean[size];
}
ant_ = new Ant(size / 2, size / 2);
setBackground(Color.WHITE);
setForeground(Color.BLACK);
setSize(size * ANT_SIZE, size * ANT_SIZE);
setPreferredSize(getSize());
}
public boolean tick() {
int y = ant_.getY();
int x = ant_.getX();
boolean b = land_[y][x];
land_[y][x] = !b;
ant_.next(b);
y = ant_.getY();
if (y < 0 || size_ <= y) {
return false;
}
x = ant_.getX();
if (x < 0 || size_ <= x) {
return false;
}
return true;
}
public void reset() {
for (int y = 0; y < land_.length; y++) {
for (int x = 0; x < land_[y].length; x++) {
land_[y][x] = false;
}
}
ant_ = new Ant(size_ / 2, size_ / 2);
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setBackground(Color.WHITE);
g2d.setColor(Color.BLACK);
g2d.clearRect(0, 0, this.getWidth(), this.getHeight());
for (int y = 0; y < land_.length; y++) {
for (int x = 0; x < land_[y].length; x++) {
if (land_[y][x]) {
g2d.fillRoundRect(x * ANT_SIZE, y * ANT_SIZE, ANT_SIZE, ANT_SIZE, 1, 1);
}
}
}
g2d.drawString(String.format("turn: %d", ant_.getCounter()), 2, 12);
}
}
|
アリ1匹につき1スレッドで。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
using System.Threading;
namespace doukaku276
{
class Program
{
const int ANT_NUM = 10;
const int FIELD_WIDTH = 100;
const int FIELD_HEIGHT = 100;
static Random rnd = new Random();
static void Main(string[] args)
{
new FormMain().ShowDialog();
}
class Ant
{
int x, y;
int direction;//0:↑ 1:→ 2:↓ 3:←
Color color;
public Ant()
{
x = rnd.Next(FIELD_WIDTH);
y = rnd.Next(FIELD_HEIGHT);
direction = rnd.Next(4);
color = Color.FromArgb(rnd.Next(254), rnd.Next(255), rnd.Next(255));
}
public void step(Bitmap bmp)
{
bool b = bmp.GetPixel(x, y).R != 255; // 黒? R=255を白とする
direction = (direction + (b ? 1 : 3)) % 4; // 回転
bmp.SetPixel(x, y, b ? Color.White : color); // 色反転
// 前進
switch (direction)
{
case 0: y--; break;
case 1: x++; break;
case 2: y++; break;
case 3: x--; break;
}
if (x < 0) x += FIELD_WIDTH;
if (x >= FIELD_WIDTH) x -= FIELD_WIDTH;
if (y < 0) y += FIELD_HEIGHT;
if (y >= FIELD_HEIGHT) y -= FIELD_HEIGHT;
}
}
class FormMain : Form
{
Bitmap bmp = new Bitmap(FIELD_WIDTH, FIELD_HEIGHT);
List<Thread> ants = new List<Thread>();
public FormMain()
{
lock (bmp)
{
for (int y = 0; y < FIELD_HEIGHT; y++)
for (int x = 0; x < FIELD_WIDTH; x++)
bmp.SetPixel(x, y, Color.White);
}
this.Size = new Size(FIELD_WIDTH * 3 + 100, FIELD_HEIGHT * 3 + 100);
this.CenterToScreen();
this.DoubleBuffered = true;
}
void update()
{
Ant ant = new Ant();
while (true)
{
lock (bmp)
{
ant.step(bmp);
}
this.Invalidate();
Thread.Sleep(1);
}
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
foreach(var t in ants) t.Abort();
}
protected override void OnPaint(PaintEventArgs e)
{
//base.OnPaint(e);
Graphics g = e.Graphics;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
lock (bmp)
{
g.DrawImage(bmp, 30, 30, FIELD_WIDTH * 3, FIELD_HEIGHT * 3);
}
g.DrawString(string.Format("ants:{0}", ants.Count), this.Font, Brushes.Black, 40, FIELD_HEIGHT * 3);
}
protected override void OnClick(EventArgs e)
{
var thread = new Thread(new ThreadStart(update));
thread.Start();
ants.Add(thread);
}
}
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | use strict;
use warnings;
use Tk;
use Tk::MsgBox;
use utf8;
my $w = shift || 100;
my $h = shift || $w;
my $pxsize = 4;
my $init_pattern_subs = create_pattern_subs();
my $pattern = shift;
my $pattarn_sub = $pattern && exists $init_pattern_subs->{$pattern}
? $init_pattern_subs->{$pattern}
: sub { 0 };
my $mw = Tk::MainWindow->new(-title => 'ラングトンの蟻');
my $control_frame = $mw->Frame;
$control_frame->pack(
-side => 'bottom',
-anchor => 's',
-fill => 'x',
);
my $drawing_frame = $mw->Frame;
$drawing_frame->pack(
-side => 'top',
-anchor => 'n',
-fill => 'both',
);
my $canvas = $drawing_frame->Canvas(
-width => $w * $pxsize, -height => $h * $pxsize,
-background => 'white',
);
my @field;
for my $x ( 0 .. $w-1 ) {
for my $y ( 0 .. $h-1 ) {
$field[$x][$y] = $canvas->createRectangle(
(map{$_ * $pxsize} $x,$y,$x+1,$y+1),
-fill => 'white'
);
}
}
$canvas->pack(-fill => 'both');
my $start_button = $control_frame->Button(
-text => 'START',
-command => \&start_proc,
-state => 'normal',
);
$start_button->pack(-anchor => 'w', -side => 'left');
my $stop_button = $control_frame->Button(
-text => 'STOP',
-command => \&stop_proc,
-state => 'disabled',
);
$stop_button->pack(-anchor => 'w', -side => 'left', -padx => 20);
my $stat = step_label(0,0,0);
my $stat_label = $control_frame->Label(
-textvariable => \$stat
);
$stat_label->pack(-anchor => 'w', -side => 'left', -padx => 30);
Tk->MainLoop;
sub create_pattern_subs
{
+{
ichimatsu => sub { (($_[0]/2) ^ ($_[1]/2)) & 0x1; },
lines => sub { ($_[0]/12) & ($_[1]/2) & 0x1; },
random => sub { !int rand 3 },
};
}
sub step_label
{
sprintf '%#8d steps (%#3d, %#3d)', @_;
}
{
my ($count,$x,$y,$d,$timer);
sub start_proc
{
$stop_button->configure(-state => 'normal');
$start_button->configure(-state => 'disabled');
$count = 0;
$x = int(rand($w/2) + $w/4);
$y = int(rand($h/2) + $h/4);
$d = int(rand(4));
for my $x_ ( 0 .. $w-1 ) {
for my $y_ ( 0 .. $h-1 ) {
$canvas->itemconfigure($field[$x_][$y_],
-fill => $pattarn_sub->($x_,$y_) ? 'black' : 'white');
}
}
$timer = $mw->repeat(10, \&next_step);
}
sub stop_proc
{
$stop_button->configure(-state => 'disabled');
$start_button->configure(-state => 'normal');
$mw->afterCancel($timer);
}
sub next_step
{
my $bit = $canvas->itemcget($field[$x][$y], '-fill') ne 'white';
$canvas->itemconfigure($field[$x][$y], -fill => ($bit ? 'white' : 'black'));
$d = ($bit ? ++$d : --$d) % 4;
if ( $d == 0 ) { --$x; }
elsif ( $d == 1 ) { ++$y; }
elsif ( $d == 2 ) { ++$x; }
elsif ( $d == 3 ) { --$y; }
if ( $x < 0 || $x >= $w || $y < 0 || $y >= $h ) {
stop_proc;
my $d = $mw->MsgBox(-title => 'END', -message => 'しゅーりょー', -type => 'ok');
$d->Show;
return;
}
$stat = step_label( ++$count, $x, $y);
}
}
|
おお。Perl/Tkをあまり使ったことがなかったので、Perlでの投稿を期待して待ってました。
ActionScriptで作成してみました。 30Frame/secでは遅かったので、1Frameに10Step進ませています。
ブラウザ上で確認できるようにwonderflにもおいておきます。
http://wonderfl.net/code/519de9f580e291a6309e2e8df3d5e8319a647433
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | package {
// Yet another implimentation of Langton's ant
// This is made for "どう書く?org", http://ja.doukaku.org/276/
// This code is written by xsd.
import flash.display.Sprite;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.geom.Rectangle;
import flash.events.Event;
import flash.text.TextField;
import flash.text.TextFormat;
[SWF(width="465", height="465", backgroundColor="0xFFFFFF", frameRate="30")]
public class LangtonsAnt extends Sprite {
private const SCREEN_WIDTH:int = 465;
private const SCREEN_HEIGHT:int = 465;
private const CELL_SIZE:int = 4;
private const STEP:int = 10;
private var bm:BitmapData;
private var bmp:Bitmap;
private var tf:TextField;
private var xAnt:int = 200;
private var yAnt:int = 200;
private var vx:int = 1;
private var vy:int = 0;
private var count:int = 0;
public function LangtonsAnt():void {
bm = new BitmapData(SCREEN_WIDTH, SCREEN_HEIGHT, false, 0xFFFFFF);
bmp = new Bitmap(bm);
this.addChild(bmp);
tf = new TextField();
tf.defaultTextFormat = new TextFormat("_Serif", 24, 0, true);
tf.x = 0; tf.y = SCREEN_HEIGHT - 32; tf.width = SCREEN_HEIGHT; tf.height = 32;
tf.selectable = false;
this.addChild(tf);
this.addEventListener(Event.ENTER_FRAME, update);
}
public function update(event:Event):void {
bm.lock();
var t:int, i:int;
for (i = 0; i < STEP; i++) {
xAnt += vx * CELL_SIZE;
yAnt += vy * CELL_SIZE;
var a:int = bm.getPixel(xAnt, yAnt);
if (a == 0) {
bm.fillRect(new Rectangle(xAnt, yAnt, CELL_SIZE, CELL_SIZE), 0xFFFFFF);
t = vx; vx = vy; vy = -t;
} else {
bm.fillRect(new Rectangle(xAnt, yAnt, CELL_SIZE, CELL_SIZE), 0x000000);
t = vy; vy = vx; vx = -t;
}
bm.unlock();
if (xAnt <= CELL_SIZE || xAnt >= SCREEN_WIDTH - CELL_SIZE ||
yAnt <= CELL_SIZE || yAnt >= SCREEN_HEIGHT - CELL_SIZE) {
this.removeEventListener(Event.ENTER_FRAME, update);
break;
}
count++;
tf.text = "Step: "+count;
}
}
}
}
|
Ruby でシンプルに書いてみました。
入力された数字の分だけどんどん世代を進めます。qが入力されたら終わりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | #
# World.rb
# iLangAnt
#
# Created by 石井 大海 on 09/07/17.
# Copyright (c) 2009 __MyCompanyName__. All rights reserved.
#
class Ant
attr_reader :world, :x, :y, :generation
def pos()
return [x,y]
end
def initialize(world=World.new, pos_x=40, pos_y=60)
@world = world
@x = pos_x
@y = pos_y
@direction = [0, -1]
@generation = 0
end
def position()
@world.at(@x, @y)
end
def step()
@x += @direction[0]
@y += @direction[1]
return nil unless position()
position.toggle()
if @world.black?(@x,@y)
@direction = [-@direction[1], @direction[0]]
else
@direction = [@direction[1], -@direction[0]]
end
@generation += 1
end
end
class World
attr_reader :height, :width
class <<self
def define_block_delegate(mtd)
define_method(mtd) {|x,y|
at(x,y).__send__(mtd)
}
end
end
def initialize(height=100, width=100, wrap=true)
@world = Array.new(height){ Array.new(width){ Block.new } }
@height, @width = height, width
@wrap = wrap
end
def at(x, y)
unless (0...@height).include?(y) && (0...@width).include?(x)
if @wrap
x, y = x % width, y % height
else
return nil
end
end
@world[y][x]
end
define_block_delegate :white?
define_block_delegate :black?
define_block_delegate :color
def inspect()
@world.map{|r| r.map(&:inspect).join("")}.join("\n")
end
end
class Block
attr_reader :state
alias color state
WHITE = true
BLACK = false
def initialize(state=WHITE)
@state = state
end
def toggle
@state = !@state
end
def set_white
@state = WHITE
end
def set_black
@state = BLACK
end
def black?
@state
end
def white?
!@state
end
def inspect()
case @state
when WHITE
" □ "
when BLACK
" ■ "
end
end
end
if $0 == __FILE__
w, h ,= ARGV
w ||= h ||= 20
wd = World.new(w, h)
ant = Ant.new(wd, w*6/10, h*4/10)
while (print"> ";gets)
break if $_ =~ /^q/i
count = 1 unless (count = $_.to_i) > 0
count.times {
ant.step
p ant.generation
p wd
}
end
end
|
自分のマシンは CPUが Pentium 4 (2.66GHz)、メモリが 512MBとリ ソースが貧弱なためか、うまく動作してくれませんでした。 なので、 #9331を参考に table要素を利用したものへと書き直して みました。 念のため、以下のブラウザーで動作を確認してあります。 Firefox 2.0.0.6 Internet Explorer 6.0.29 Google Chrome 1.0.154.48 Opera 9.23 Safari 3.1.2 Firefox, Google Chrome, Safariは CPUをほとんど食わないのです が、 Internet Explorerと Operaはともに CPU使用率が高く、動作 も遅かったです。(実装の違いですかね? )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | <html>
<head>
<style type="text/css">
.C_0 { background-color : #ffffff; height : 3px; width : 3px; }
.C_1 { background-color : #000000; height : 3px; width : 3px; }
</style>
<script type="text/javascript">
Function.prototype.repeat =
function (t, o) {
var _ = this;
return setInterval(function () { _.apply(o); }, t);
};
var $ = function (i) { return document.getElementById(i); };
var USER_AGENT = navigator.userAgent.toLowerCase();
var CLASS_NAME = (USER_AGENT.indexOf('msie') > -1) ? 'className' : 'class';
var Colony =
function (w, h) {
this.w = w;
this.h = h;
this.generate =
function () {
var i, j;
document.write('<table border="0" cellpadding="0" cellspacing="0">');
for (j = 0; j < this.h; j++) {
document.write('<tr>');
for (i = 0; i < this.w; i++) document.write('<td id="' + i + '_' + j + '" class="C_0"></td>');
document.write('</tr>');
}
document.write('</table>');
};
};
var Ant =
function (x, y, w, h) {
this.x = x;
this.y = y;
this.dx = -1;
this.dy = 0;
this.w = w;
this.h = h;
this.id = 0;
this.move =
function () {
var C = $(this.x + '_' + this.y);
var c;
var t;
if (this.x < 0 || this.x >= this.w || this.y < 0 || this.y >= this.h) {
alert('DEAD END ...');
clearTimeout(this.id);
return;
}
if (c = ((C.getAttribute(CLASS_NAME)).split('_')[1] ^ 1)) { // 反転
t = this.dx;
this.dx = this.dy;
this.dy = -t;
} else {
t = this.dx;
this.dx = -this.dy;
this.dy = t;
}
C.setAttribute(CLASS_NAME, 'C_' + c);
this.x += this.dx;
this.y += this.dy;
if (!this.id) this.id = this.move.repeat(10, this);
};
};
</script>
</head>
<body>
<script type="text/javascript">
var W = 100, H = 100;
new Colony(W, H).generate();
new Ant(50, 50, W, H).move();
</script>
</body>
</html>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | def ant(world_size, x_pos = world_size / 2, y_pos = world_size / 2)
world = Array.new
generation = 0
direc = {}; direc[:x] = 0; direc[:y] = 1
world_size.times{world << [true] * world_size}
while true
system "clear"
p generation += 1
world[x_pos][y_pos] = !world[x_pos][y_pos]
world.each{|i|
i.each{|k| printf("%s ",k ? :□ : :■)}
p ''
}
sleep 0.1
if world[x_pos][y_pos] then
direc[:x], direc[:y] = direc[:y], -direc[:x]
else
direc[:x], direc[:y] = -direc[:y], direc[:x]
end
x_pos = (x_pos + direc[:x]) % world_size
y_pos = (y_pos + direc[:y]) % world_size
end
end
world_size = 20
ant(world_size)
|
OpenGL/GLUTで。
ウィンドウの表示と描画がコードの半分以上orz。
作成はMac OS Xでしています。他の環境の場合には調整してください。
コンパイルは、
g++ -ansi -Wall -framework OpenGL -framework GLUT -framework Foundation -o doukaku276 doukaku276.cpp
終了条件はないので、ESCキーかQキーで終了させてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | #include <bitset>
#include <GLUT/glut.h>
class Field
{
public:
virtual ~Field() {}
virtual bool operator () (int x, int y) const = 0;
virtual void set(int x, int y) = 0;
virtual void unset(int x, int y) = 0;
};
template<int Width, int Height>
class ConcreteField : public Field
{
public:
ConcreteField() : bits_() {}
bool operator () (int x, int y) const { return bits_[y * Width + x]; }
void set(int x, int y) { bits_[y * Width + x] = true; }
void unset(int x, int y) { bits_[y * Width + x] = false; }
private:
std::bitset<Width * Height> bits_;
};
class Direction
{
public:
static const int North = 0;
static const int East = 1;
static const int South = 2;
static const int West = 3;
Direction(int dir) : dir_(dir) {}
void set(int dir) { dir_ = dir; }
int get() const { return dir_; }
void turnRight()
{
dir_ += 1;
dir_ %= 4;
}
void turnLeft()
{
dir_ += 3;
dir_ %= 4;
}
private:
int dir_;
};
class Ant
{
public:
Ant(int x, int y, Direction direction) : x_(x), y_(y), direction_(direction) {}
void move(Field& field)
{
if(field(x_, y_))
{
field.unset(x_, y_);
direction_.turnRight();
moveForward();
}
else
{
field.set(x_, y_);
direction_.turnLeft();
moveForward();
}
}
void moveForward()
{
switch(direction_.get())
{
case Direction::North: --y_; break;
case Direction::East: ++x_; break;
case Direction::South: ++y_; break;
case Direction::West: --x_; break;
default: break;
}
}
private:
int x_;
int y_;
Direction direction_;
};
static const int Interval = 0;
static const int Width = 320;
static const int Height = 240;
static ConcreteField<Width, Height> field;
static Ant ant(Width / 2, Height / 2, Direction::North);
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
static const GLdouble black[] = { 0.0, 0.0, 0.0 };
glColor3dv(black);
glBegin(GL_POINTS);
for(int y = 0; y < Height; ++y)
{
for(int x = 0; x < Width; ++x)
{
if(field(x, y))
{
glVertex2f(x, y);
}
}
}
glEnd();
glutSwapBuffers();
}
void resize(int w, int h)
{
glViewport(0, 0, w, h);
glLoadIdentity();
glOrtho(-0.5, w - 0.5, h - 0.5, -0.5, -1.0, 1.0);
}
void key(unsigned char key, int x, int y)
{
switch(key)
{
case 'q':
case 'Q':
case '\033':
std::exit(0);
break;
default:
break;
}
}
void timer(int n)
{
ant.move(field);
display();
glutTimerFunc(Interval, timer, 0);
}
void initWindow(int argc, char* argv[])
{
glutInitWindowPosition(100, 100);
glutInitWindowSize(Width, Height);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutCreateWindow("doukaku#276");
glClearColor(1.0, 1.0, 1.0, 1.0);
}
void initEvent()
{
glutDisplayFunc(display);
glutReshapeFunc(resize);
glutKeyboardFunc(key);
glutTimerFunc(Interval, timer, 0);
}
int main(int argc, char* argv[])
{
initWindow(argc, argv);
initEvent();
glutMainLoop();
return 0;
}
|
Excel VBAにて、ごくシンプルに。
Excelを以下のように調整すると、実行結果が見やすくなります。
- 行の高さと列の幅を調整して、全てのセルを正方形にする。
- 表示倍率を小さくして、なるべく多くのセルが画面内に収まるようにする
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | Option Explicit
'// [定数] 蟻の方向
Const DIR_UP = 0 '上向き
Const DIR_RIGHT = 1 '右向き
Const DIR_DOWN = 2 '下向き
Const DIR_LEFT = 3 '左向き
'// [定数] 色
Const COLOR_WHITE = &HFFFFFF '白
Const COLOR_BLACK = &H0 '黒
'// ラングトンの蟻
Sub Langtons_ant()
Dim r_Ant As Long '// 蟻の位置(行番号)
Dim c_Ant As Long '// 蟻の位置(行番号)
Dim dir_Ant As Integer '// 蟻の方向
'////////////////////////////////////////////////////////////////////
' 初期処理
'////////////////////////////////////////////////////////////////////
'// 蟻の位置を決める。
r_Ant = 50
c_Ant = 50
'// 蟻の方向を決める。
dir_Ant = DIR_UP '上向き
'////////////////////////////////////////////////////////////////////
' 主処理
'////////////////////////////////////////////////////////////////////
'// 蟻がシート内にいる間、以下を繰り返す。
Do While 0 < c_Ant And c_Ant <= Columns.Count _
And 0 < r_Ant And r_Ant <= Rows.Count
'// 蟻がいるマスの色による場合分け。
If Cells(r_Ant, c_Ant).Interior.Color = COLOR_BLACK Then
'// マスの色が黒:
'// 蟻を90°右に方向転換する。
dir_Ant = (dir_Ant + 1) Mod 4
'// マスの色を白に変える。
Cells(r_Ant, c_Ant).Interior.Color = COLOR_WHITE
Else
'// マスの色が白:
'// 蟻を90°左に方向転換する。
dir_Ant = (dir_Ant + 3) Mod 4
'// マスの色を黒に変える。
Cells(r_Ant, c_Ant).Interior.Color = COLOR_BLACK
End If
'// 蟻を一歩進める。
Select Case dir_Ant
Case DIR_UP
'// 蟻が上向き:
r_Ant = r_Ant - 1
Case DIR_RIGHT
'// 蟻が右向き:
c_Ant = c_Ant + 1
Case DIR_DOWN
'// 蟻が下向き:
r_Ant = r_Ant + 1
Case DIR_LEFT
'// 蟻が左向き:
c_Ant = c_Ant - 1
End Select
Loop
End Sub
|
Excel VBAにて、複数の蟻が動くようにしました。(#9388の変更)
Excelを以下のように調整すると、実行結果が見やすくなります。
- 行の高さと列の幅を調整して、全てのセルを正方形にする。
- 表示倍率を小さくして、なるべく多くのセルが画面内に収まるようにする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | Option Explicit
'// [定数] 蟻の方向
Const DIR_UP = 0 '上向き
Const DIR_RIGHT = 1 '右向き
Const DIR_DOWN = 2 '下向き
Const DIR_LEFT = 3 '左向き
'// [定数] 色
Const COLOR_WHITE = &HFFFFFF '白
Const COLOR_BLACK = &H0 '黒
'// [構造体] 蟻
Type typ_Ant
r As Long '// 蟻の位置(行番号)
c As Long '// 蟻の位置(列番号)
dir As Long '// 蟻の方向
died As Boolean '// 死亡フラグ
End Type
'// ラングトンの蟻(複数版)
Sub Langtons_ant_s()
Dim ants() As typ_Ant '蟻
Dim i As Integer 'インデックス
Dim AllDied As Boolean '全ての蟻が死亡したか?
'////////////////////////////////////////////////////////////////////
' 初期処理
'////////////////////////////////////////////////////////////////////
'// 蟻の数を決める。
ReDim ants(5)
'// 蟻1匹目
ants(0).r = 70
ants(0).c = 100
ants(0).dir = DIR_UP
'// 蟻2匹目
ants(1).r = 100
ants(1).c = 100
ants(1).dir = DIR_UP
'// 蟻3匹目
ants(2).r = 30
ants(2).c = 120
ants(2).dir = DIR_RIGHT
'// 蟻4匹目
ants(3).r = 40
ants(3).c = 90
ants(3).dir = DIR_DOWN
'// 蟻5匹目
ants(4).r = 40
ants(4).c = 10
ants(4).dir = DIR_LEFT
'// 蟻6匹目
ants(5).r = 70
ants(5).c = 100
ants(5).dir = DIR_UP
'////////////////////////////////////////////////////////////////////
' 主処理
'////////////////////////////////////////////////////////////////////
'// いずれかの蟻が生きている間、以下を繰り返す。
Do While True
AllDied = True
For i = 0 To UBound(ants)
If Not ants(i).died Then
AllDied = False
Exit For
End If
Next
If AllDied Then
'// 全ての蟻が死亡している。
'// 処理を中断する。
Exit Do
End If
'// 生きているすべての蟻について、以下を繰り返す。
For i = 0 To UBound(ants)
If Not ants(i).died Then
'// 蟻がいるマスの色による場合分け。
If Cells(ants(i).r, ants(i).c).Interior.Color = COLOR_BLACK Then
'// マスの色が黒:
'// 蟻を90°右に方向転換する。
ants(i).dir = (ants(i).dir + 1) Mod 4
'// マスの色を白に変える。
Cells(ants(i).r, ants(i).c).Interior.Color = COLOR_WHITE
Else
'// マスの色が白:
'// 蟻を90°左に方向転換する。
ants(i).dir = (ants(i).dir + 3) Mod 4
'// マスの色を黒に変える。
Cells(ants(i).r, ants(i).c).Interior.Color = COLOR_BLACK
End If
'// 蟻を一歩進める。
Select Case ants(i).dir
Case DIR_UP
'// 蟻が上向き:
ants(i).r = ants(i).r - 1
Case DIR_RIGHT
'// 蟻が右向き:
ants(i).c = ants(i).c + 1
Case DIR_DOWN
'// 蟻が下向き:
ants(i).r = ants(i).r + 1
Case DIR_LEFT
'// 蟻が左向き:
ants(i).c = ants(i).c - 1
End Select
'// 蟻がシート内にいることを確認する。
If Not (0 < ants(i).c And ants(i).c <= Columns.Count _
And 0 < ants(i).r And ants(i).r <= Rows.Count) Then
'// シート内にいない:
'// 蟻の死亡フラグをオンにする。
ants(i).died = True
End If
End If
Next
Loop
End Sub
|
初期状態の背景色を白黒選択可能にしました。
アリの初期状態の向きはランダムにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | open System
open System.Windows.Forms
open System.Drawing
let size = 300
let (white, black) = (-1, 1)
let dirTable = [|(1, 0); (0, 1); (-1, 0); (0, -1)|]
let dirTextTable = [|"右"; "下"; "左"; "上"|]
type Langton'sAntForm() as this =
inherit Form()
[<DefaultValue>]
val mutable ant : int -> int[,] -> Async<unit>
let bitmap = new Bitmap(size, size)
let picture = new PictureBox(Image = bitmap, Size = new Size(size, size), Location = new Point(0, 30))
let button = new Button(Size = new Size(50, 30), Location = new Point(0, 0), Text = "Start")
let checkbox = new CheckBox(Size = new Size(70, 30), Location = new Point(60, 0), Text = "黒背景")
let labelDir = new Label(Size = new Size(80, 30), Location = new Point(140, 0), TextAlign = ContentAlignment.MiddleLeft)
let labelStep = new Label(Size = new Size(60, 30), Location = new Point(230, 0), TextAlign = ContentAlignment.MiddleLeft)
do this.Controls.AddRange([|(picture :> Control); (button :> Control); (checkbox :> Control); (labelDir :> Control); (labelStep :> Control)|])
do this.Text <- "ラングトンのアリ"
do this.ClientSize <- new Size(size, size + 30)
do this.FormBorderStyle <- FormBorderStyle.Fixed3D
do this.MaximizeBox <- false
do button.Click.Add(fun _ ->
let dir = (new Random()).Next(4)
labelDir.Text <- "初期方向:" ^ dirTextTable.[dir]
let (place, color) = if checkbox.Checked then (black, Color.Black) else (white, Color.White)
[for x in 0..(size - 1) -> [for y in 0..(size - 1) -> (x, y)]] |> List.concat
|> List.iter(fun (x, y) -> bitmap.SetPixel(x, y, color))
Array2D.create size size place |> this.Ant dir |> Async.Start
button.Enabled <- false)
member this.Ant with get() = this.ant and set(value) = this.ant <- value
member this.Print (x, y) place step =
try
if this.InvokeRequired then
this.Invoke(new MethodInvoker(fun () ->
bitmap.SetPixel(x, y, if place = white then Color.White else Color.Black)
labelStep.Text <- string step
this.Refresh()))
|> ignore
with
| :? ObjectDisposedException -> ()
let rec loop step (x, y) dir (field : int[,]) (form : Langton'sAntForm) =
let rotate dir place = (dir + place + 4) % 4
let turnover = ( * ) -1
let move (x, y) dir =
let (dx, dy) = dirTable.[dir] in (x + dx, y + dy)
let (|InField|OutOfField|) (x, y) =
if 0 <= x && x < size && 0 <= y && y < size then InField else OutOfField
match (x, y) with
| OutOfField -> ()
| InField ->
let newDir = rotate dir field.[x, y]
field.[x, y] <- turnover field.[x, y]
form.Print (x, y) field.[x, y] step
loop (step + 1) (move (x, y) newDir) newDir field form
[<STAThread()>]
do
use form = new Langton'sAntForm()
form.Ant <- fun dir field -> async { loop 1 (size / 2, size / 2) dir field form }
Application.EnableVisualStyles()
Application.Run(form) |> ignore
|
DOMのあるノード直下に非常に多数のノードを作る(兄弟ノードが多すぎる)・それらのノードにアクセスするというのは、速度低下の原因になりがちです。どのくらいの数からどこに影響が出るかは実装によるでしょうが、ノード総数が少々多くなろうとも入れ子・小分けにすることで改善できる場合があります。
添付コードのように変更を加えた場合、Pen4 2.53GHzにおいて時間を計測すると次のようになりました(単位:ms ― 遅いCPUだから差が顕著になっているのであって、今時のCPUではこれほどの改善は見込めないかもしれませんが)。
Firefox 3.5.1:
キャンバス作成(ノード作成)が34,551から1,021へと大幅に改善、アリの行進(ノードアクセス)でも330,770から216,934に改善
Opera 10b1:
キャンバス作成は328から344にと若干遅くなるが、もともとあまり時間が掛かっていない。アリの行進の方は1,188,625から415,312に改善
一方で、テーブル要素にしてしまう方法も試してみましたが、手元の環境ではdiv要素で小分けとほとんど変わりませんでした。セルの色表現を、要素のクラス書き換え・クラスごとにCSSで色指定する方法も試してみたが、こちらでもほとんど変化なし。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <style type="text/css">
#canvas{
border: 1px solid #999;
width: 300px;
height: 300px;
}
/* 子divへのCSS指定を、孫divへの指定にするだけ */
#canvas div div{
width: 3px;
height: 3px;
float: left;
}
</style>
// スクリプト部の変更点はwindow.onloadのコールバックのみ
window.onload = function(){
// キャンバス作成部 セルを表すdivを入れ子に
var canvas = document.getElementById('canvas');
var k = 0;
for(var i=0; i< WORLD_SIZE; i++){
var row = document.createElement('div');
canvas.appendChild(row);
for(var j=0; j< WORLD_SIZE; j++){
var cell = document.createElement('div');
row.appendChild(cell);
earth[k] = new Cell(cell);
k++;
}
}
// 以下変更なし
lang_ant = new Ant();//蟻の誕生
lang_ant.world = earth;//地球に降り立つ
lang_ant.ageDisplay = document.getElementById('step');
document.getElementById('run').disabled = false;
}
|
Haskellで実装。要:UTF8-String
$ runhaskell langton.hs 100 100 20
とかすると、100x100マスの世界の模様を20ステップごとに表示してくれます。最後の数字を省略すると律儀に一世代ごとに印字します。
無限リストばんざい!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | import qualified System.IO.UTF8 as U
import System.Environment (getArgs, getProgName)
import Control.Monad.State
import qualified Data.Map as M
import Data.List (sortBy, groupBy)
type Point = (Int, Int)
type Direction = (Int, Int)
data Ant = Ant {pos::Point, direction::Direction} deriving Show
data Color = White | Black
data AntsState = AS { world::(M.Map Point Color), ants::[Ant], wrap :: Bool, height::Int, width::Int, generation::Int }
instance Show Color where
show Black = "■"
show White = "□"
cmp f ((_,a),_) ((_,b),_) = f a b
instance Show AntsState where
show AS{world=wd, generation=g} = "gen: " ++ show g ++ "\n" ++ (unlines $ map (concatMap(show.snd)) $ groupBy (cmp (==)) $ (sortBy (cmp compare) $ M.toList wd))
main = do args <- getArgs
pname <- getProgName
case args of
(x:y:s:_) -> mapM_ (U.putStrLn . show . head) $ iterate (drop (read s)) $ evolutions (read x) (read y)
(x:y:_) -> mapM_ (U.putStrLn . show) $ evolutions (read x) (read y)
_ -> putStrLn ("usage: " ++ pname ++ " height width [step]")
makeAnt x y = Ant {pos=(x, y), direction=(0,-1)}
makeWorld height width ants wrap = AS {
world = M.fromList [((x,y), White) | x <- [0..width-1], y <- [0..height-1]],
ants = ants,
height = height,
width = width,
generation = 0,
wrap = wrap
}
proceed :: State AntsState AntsState
proceed = do st@AS{ants=as,generation=g} <- get
a' <- mapM procAnts as
st <- get
let s = st{ants = a',generation=g+1}
put s
return s
procAnts ant@Ant{pos=p@(x,y), direction=(dx,dy)} = do
w@AS{world=wd, wrap=wr, width=wdt, height=h} <- get
let pt = ((x+dx) `mod` wdt, (y+dy) ` mod` h)
st = maybe Black id $ M.lookup pt wd
(dr, s') = case st of
Black -> ((dy, -dx), White)
_ -> ((-dy, dx), Black)
put w{world= M.insert pt s' wd}
return ant{pos = pt, direction = dr}
evolutions width height = iterate (execState proceed) (makeWorld width height [makeAnt (width*3`div`5) (height*2`div`5)] True)
|
画面のサイズや画面端での扱いについて特に規定されていないようなので、何も考えずにサックリと書いてみた。枠まで到達しても死にません。
方向転換に関する論理計算がパッと見で分かりにくいだけで、他は分かりやすいのではないかと。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | !東=0
!南=1
!西=2
!北=3
線スタイルは`透明`
必要な 間、蟻を進めて0.01秒待つ
■ブロック
・{整数}W{=5}
・{整数}H{=5}
・色取得(X,Yの)~
X*W,Y*Wを点取得
・プロット(X,YにCOLを)~
塗り色はCOL
X*W,Y*Hから(X+1)*W,(Y+1)*Hへ四角
■蟻 +ブロック
・{整数}向き{=2}
・{整数}X{=40}
・{整数}Y{=40}
・進む~
COLとは整数=X,Yの色取得
向き=(向き-2*(COL==白色)+5)%4
X,Yに(COLの反転色)をプロット
X=X-(向き==西)+(向き==東)
Y=Y-(向き==南)+(向き==北)
●反転色(COLの)
白色-COLを戻す
|
連投失礼。Wikipediaに載っていた拡張版のラングトンの蟻(色反転でなく、複数色循環)も実装してみた。まだ誰もやっていなかったかな?拡張の解釈は多分合っていると思ふ。。。
あたかも要塞のように常に左右に対称に広がっていく様子は割と面白い。(都市発展系のシミュレーションゲームのよう)
# 発展にかなり時間がかかるので、ついでに500ステップ毎に描画を反映させるようにしました。
see: ラングトンのアリ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | !ブロック幅 =3
!ブロック高さ=3
!世界幅 =150
!世界高さ=120
!東=0
!南=1
!西=2
!北=3
!色数 =4
!基準色=黒色
!STEPBY=500
ルールは『RLLR』
ルールの色配列は「{基準色},{赤色},{青色},{緑色}」を`,`で区切ったもの
母艦について
クライアントW=ブロックのW*世界幅
クライアントH=ブロックのH*世界高
スタイルは`枠固定`
基準色で画面クリア
STEPとは整数
線スタイルは`透明`
必要な 間
蟻を進める。
もしSTEP%STEPBYが0ならば
0.01秒待つ
母艦は「step: {STEP}」
STEPに1を直接足す
■ルール
・{配列}ルール配列
・テキスト ←ルール設定 デフォルト
・ルール設定(V)~
Vを文字列分解して反復
ルール配列[回数-1]=2*(対象==`R`)-1 # Rなら1、それ以外は-1
・{非公開}色配列
■ブロック
・{非公開}W{=3}
・{非公開}H{=3}
・色取得(X,Yの)~
X*W,Y*Wを点取得
・プロット(X,YにCOLを)~
塗り色はCOL
X*W,Y*Hから(X+1)*W,(Y+1)*Hへ四角
■蟻 +ブロック
・{整数}向き{=0}
・{整数}X{=75}
・{整数}Y{=50}
・進む~
COLとは整数=X,Yの色取得
COLで向きを方向転換する
COLを色循環する
X,YにCOLをプロット
Xに(向き==東)-(向き==西)を世界幅でMOD加算
Yに(向き==南)-(向き==北)を世界高でMOD加算
●方向転換する({整数}COLで{参照渡し}DIRを)
Iとは整数=ルールの色配列でCOLを配列検索
もしIが(-1)ならばI=0
DIRに(ルールのルール配列[I])を4でMOD加算する
●色循環する({参照渡し}COLを)
COLORSとは配列=ルールの色配列
Iとは整数=COLORSでCOLを配列検索
COL=COLORS[(I+1)%色数]
# Z/CZ上でAにBを直接足す
●MOD加算({参照渡し}Aに{整数}Bを{整数}Cで)
A=(A+B+C)%C
|
バッチで。
e.g.
C:\>#276.bat 10
|
** |
* * |
* * |
* * |
**** |
|
|
|
|
X = 7, Y = 5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | @echo off
setlocal enabledelayedexpansion
set s=
set t=
set x=0
set y=0
set dx=-1
set dy=0
if "%~1" == "" (echo usage: %~n0 SIZE >&2 & exit /b 1)
set /a x=%~1/2,y=%~1/2
for /l %%j in (1,1,%~1) do (
for /l %%i in (1,1,%~1) do set c[%%i][%%j]=0
)
:_
if !x! lss 1 goto BREAK
if !x! gtr %~1 goto BREAK
if !y! lss 1 goto BREAK
if !y! gtr %~1 goto BREAK
if !c[%x%][%y%]! equ 0 (
set c[%x%][%y%]=1
set t=%dx%
set dx=%dy%
set /a dy=-!t!
) else (
set c[%x%][%y%]=0
set t=%dx%
set /a dx=-!dy!
set dy=!t!
)
set /a x+=%dx%,y+=%dy%
cls
for /l %%j in (1,1,%~1) do (
set s=
for /l %%i in (1,1,%~1) do (
if !c[%%i][%%j]! equ 0 (
set s=!s!
) else (
set s=!s!*
)
)
echo ^ !s!^|
)
echo X = %x%, Y = %y%
ping -n 2 127.0.0.1 > NUL
goto _
:BREAK
endlocal & echo DEAD END ...
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | #!/usr/bin/python
# -*- coding: utf-8 -*-
# http://ja.doukaku.org/276/
import random
import sys
EAST = 0
NORTH = 90
WEST = 180
SOUTH = 270
BLACK = (0, 0, 0)
RED = (1, 0, 0)
GREEN = (0, 1, 0)
BLUE = (0, 0, 1)
class Grid(object):
def __init__(self, width=40, height=20):
self._width = width
self._height = height
self._color = {}
def width(self):
return self._width
def height(self):
return self._height
def color(self, position, color=None):
if color is not None:
self._color[tuple(position)] = color
return self._color.get(tuple(position), BLACK)
def view(self):
for y in range(0, self.height()):
for x in range(0, self.width()):
sys.stdout.write({
BLACK: ' ',
RED: '*',
GREEN: '+',
BLUE: '@',
}[self.color((x, y))])
sys.stdout.write('\n')
class Ant(object):
def __init__(
self,
grid,
color=RED,
position=[10, 10],
direction=EAST,
):
self._grid = grid
self._position = position
self._direction = direction
self._color = color
def position(self, position=None):
if position is not None:
self._position = list(position)
return self._position
def color(self, color=None):
if color is not None:
self._color = color
return self._color
def left(self):
self._direction = (self._direction + 90) % 360
def right(self):
self._direction = (self._direction - 90) % 360
def forward(self):
if self._direction == NORTH:
self._position[1] -= 1
elif self._direction == SOUTH:
self._position[1] += 1
elif self._direction == EAST:
self._position[0] -= 1
elif self._direction == WEST:
self._position[0] += 1
self._position[0] %= self._grid.width()
self._position[1] %= self._grid.height()
def step(self):
if self._color == self._grid.color(self._position):
self._grid.color(self._position, BLACK)
self.left()
else:
self._grid.color(self._position, self._color)
self.right()
self.forward()
if __name__ == '__main__':
width = int(sys.argv[1])
height = int(sys.argv[2])
num = int(sys.argv[3])
iteration = int(sys.argv[4])
grid = Grid(width=width, height=height)
ants = []
for i in range(0, num):
c = (RED, GREEN, BLUE)[i % 3]
x = int(random.random() * width)
y = int(random.random() * height)
ants.append(Ant(grid, color=c, position=[x, y]))
for i in range(0, iteration):
for ant in ants:
ant.step()
if i % (iteration / 10) == 0:
sys.stdout.write('step: %d\n' % i)
grid.view()
|
C++からの「翻訳」なのでHaskellらしくないかもしれません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | import Graphics.UI.GLUT
import Control.Exception
import System.Exit
import Data.IORef
interval = 0
windowWidth = 320::GLsizei
windowHeight = 240::GLsizei
data Direction = North | East | South | West deriving Enum
data Ant = Ant Position Direction
turnRight :: Direction -> Direction
turnRight d = toEnum (((fromEnum d) + 1) `mod` 4)
turnLeft :: Direction -> Direction
turnLeft d = toEnum (((fromEnum d) + 3) `mod` 4)
initialEnv :: (Ant, [Position])
initialEnv = (Ant (Position (windowWidth `div` 2) (windowHeight `div` 2)) East, [])
convd :: (Integral a) => a -> GLdouble
convd = fromInteger.toInteger
forward (Position x y) North = Position x (y - 1)
forward (Position x y) East = Position (x + 1) y
forward (Position x y) South = Position x (y + 1)
forward (Position x y) West = Position (x - 1) y
draw env = do
(_, ps) <- readIORef env
clearColor $= Color4 1.0 1.0 1.0 1.0
clear [ColorBuffer]
color $ Color3 (0.0::Double) 0.0 0.0
renderPrimitive Points $ mapM_ (¥ (Position x y) -> vertex $ Vertex2 x y) ps
swapBuffers
update (Ant pos dir, ps) =
if elem pos ps
then (Ant (forward pos (turnRight dir)) (turnRight dir), [p | p <- ps, p /= pos])
else (Ant (forward pos (turnLeft dir)) (turnLeft dir), pos:ps)
-- event handlers
display env = do
draw env
reshape (Size w h) = do
viewport $= (Position 0 0, Size w h)
loadIdentity
ortho (-0.5) ((convd w) - 0.5) ((convd h) - 0.5) (-0.5) (-1.0) (1.0)
keyboardMouse key keystate modifiers position = do
case key of
Char 'q' -> throwIO $ ExitException ExitSuccess
_ -> return ()
timer env = do
modifyIORef env $ update
draw env
addTimerCallback interval $ timer env
main = do
env <- newIORef initialEnv
getArgsAndInitialize
initialDisplayMode $= [RGBAMode, DoubleBuffered]
initialWindowPosition $= Position 100 100
initialWindowSize $= Size windowWidth windowHeight
createWindow "doukaku#276"
displayCallback $= display env
reshapeCallback $= Just reshape
keyboardMouseCallback $= Just keyboardMouse
addTimerCallback interval $ timer env
mainLoop
|
Python + Tkinter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | #!/usr/bin/python
# -*- coding: utf-8 -*-
# http://ja.doukaku.org/276/
import random
import sys
import Tkinter as Tk
DOT_SIZE = 5
class Ant(object):
def __init__(self, grid, x, y, direction, color):
self._grid = grid
self.x = x
self.y = y
self.direction = direction
self.color = color
def left(self):
self.direction = (self.direction + 90) % 360
def right(self):
self.direction = (self.direction - 90) % 360
def forward(self):
diff = { # 0:EAST, 90:NORTH 180:SOUTH 270:WEST
0: (1, 0),
90: (0, -1),
180: (-1, 0),
270: (0, 1),
}[self.direction]
self.x = (self.x + diff[0]) % self._grid.width
self.y = (self.y + diff[1]) % self._grid.height
def is_my_color(self):
return self.color == self._grid.get_color(self.x, self.y)
def black(self):
self._grid.delete_color(self.x, self.y)
def my_color(self):
self._grid.update_color(self.x, self.y, self.color)
def step(self):
if self.is_my_color():
self.black()
self.left()
else:
self.my_color()
self.right()
self.forward()
class Grid(Tk.Canvas):
def __init__(self, parent, width=40, height=20):
self.width = width
self.height = height
self._color = {}
xw = DOT_SIZE * self.width
xh = DOT_SIZE * self.height
Tk.Canvas.__init__(self, parent, background='black', width=xw,
height=xh)
def color(self, x, y):
return self._color.get((x, y), {'id': None, 'color': 'black'})
def get_color(self, x, y):
return self.color(x, y)['color']
def delete_color(self, x, y):
old = self.color(x, y)
if old['id'] is not None:
self.delete(old['id'])
del self._color[(x, y)]
def update_color(self, x, y, color):
old = self.color(x, y)
if old['id'] is None:
xx0 = x * DOT_SIZE
xy0 = y * DOT_SIZE
xx1 = xx0 + DOT_SIZE
xy1 = xy0 + DOT_SIZE
id = self.create_rectangle(xx0, xy0, xx1, xy1, fill=color,
outline=color)
self._color[(x, y)] = {'id': id, 'color': color}
else:
self.itemconfigure(old['id'], fill=color, outline=color)
class Frame(Tk.Frame):
def __init__(self, width, heigth, num_ant, master=None):
Tk.Frame.__init__(self, master)
self.master.title('Rangton Ant')
self._grid = Grid(self, width=width, height=height)
self._grid.pack()
def _rand_int(n):
return int(random.random() * n)
def _make_ant(i):
c = ('red', 'yellow', 'green', 'cyan', 'blue', 'cyan',
'magenta', 'white')[i % 7]
d = (0, 90, 180, 270)[_rand_int(4)]
x = _rand_int(width)
y = _rand_int(height)
return Ant(self._grid, x, y, d, c)
self._ants = map(_make_ant, range(num_ant))
self.move()
def move(self):
for ant in self._ants:
ant.step()
self.after(1, self.move)
if __name__ == '__main__':
width = int(sys.argv[1])
height = int(sys.argv[2])
num_ant = int(sys.argv[3])
f = Frame(width, height, num_ant)
f.pack()
f.mainloop()
|
出題者によるデモが初期読み込み時に重かったので、改編してみました。
おまけにアリ自体も最大5匹まで増量可能だったり。
ちなみにOperaだとCSSのエラーで動かないっぽいのが難点ですorz
see: サンプル
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | <html>
<head>
<meta http-equiv="content-type" content="text/html;charset=shift_jis">
<title>Langton's Ant</title>
<script type="text/javascript"><!-- //
langton={};
langton.Direction=function(x,y){ this.x=(x)?x:0; this.y=(y)?y:0; };
langton.Direction.prototype={
x : 0,
y : 0,
equals : function(test){
return (this.x==test.x)&&(this.y==test.y);
},
change : function(dif){
var val=langton.Direction.next(this,dif);
this.x=val.x;
this.y=val.y;
return this;
}
};
langton.Direction.NORTH=new langton.Direction(0,-1);
langton.Direction.SOUTH=new langton.Direction(0,1);
langton.Direction.EAST=new langton.Direction(1,0);
langton.Direction.WEST=new langton.Direction(-1,0);
langton.Direction.CYCLE=[
langton.Direction.NORTH,
langton.Direction.EAST ,
langton.Direction.SOUTH,
langton.Direction.WEST
];
langton.Direction.FORWORD=0;
langton.Direction.RIGHT=1;
langton.Direction.BACKWORD=2;
langton.Direction.LEFT=3;
langton.Direction.next=function(current,change){
var i = 0;
for( ; i < langton.Direction.CYCLE.length; i++){
if(current.equals(langton.Direction.CYCLE[i])){ break; }
}
i+=change;
i%=langton.Direction.CYCLE.length;
return langton.Direction.CYCLE[i];
};
langton.Ant=function(){};
langton.Ant.prototype={
x : -1,
y : -1,
direction : null,
color : null,
go : function(lr){
this.direction=this.direction.change(lr);
this.x+=this.direction.x;
this.y+=this.direction.y;
if(this.x==langton.Canvas.rowsize){ this.x=0; }
if(this.x<0){ this.x=langton.Canvas.rowsize-1; }
if(this.y==langton.Canvas.rowsize){ this.y=0; }
if(this.y<0){ this.y=langton.Canvas.rowsize-1; }
return this;
},
eat : function(cell,isblack){
var color = (isblack)?this.color:langton.Canvas.BLACK;
cell.style.backgroundColor=color;
},
action : function(cells){
var index = this.y*langton.Canvas.rowsize + this.x;
if(!cells[index]){
cells[index]=langton.Canvas.prepareCell();
cells[index].style.position="absolute";
cells[index].style.top=(this.x*3)+"px";
cells[index].style.left=(this.y*3)+"px";
}
var isblack=(cells[index].style.backgroundColor==langton.Canvas.BLACK);
this.eat(cells[index],isblack);
var choice = (isblack) ? langton.Direction.RIGHT
: langton.Direction.LEFT;
this.go(choice);
return this;
}
};
langton.Canvas={};
langton.Canvas.body=null;
langton.Canvas.counter=null;
langton.Canvas.rowsize=100;
langton.Canvas.cells=[];
langton.Canvas.clear=function(){
if(langton.Canvas.counter){
langton.Canvas.counter.value=0;
langton.Canvas.counter=null;
}
if(langton.Canvas.body){
langton.Canvas.body.innerHTML="";
langton.Canvas.body=null;
}
langton.Canvas.ants=[];
langton.Canvas.cells=[];
};
langton.Canvas.ants=[];
langton.Canvas.BLACK="black";
langton.Canvas.WHITE="white";
langton.Canvas.BLUE="blue";
langton.Canvas.RED="red";
langton.Canvas.YELLOW="yellow";
langton.Canvas.GREEN="green";
langton.Canvas.COLORS=[
langton.Canvas.WHITE,
langton.Canvas.BLUE,
langton.Canvas.RED,
langton.Canvas.YELLOW,
langton.Canvas.GREEN
];
langton.Canvas.prepareCell=function(){
var div = document.createElement("div");
langton.Canvas.body.appendChild(div);
div.style.width="3px";
div.style.height="3px";
div.style.backgroundColor=langton.Canvas.BLACK;
return div;
};
langton.Canvas.addAnt=function(){
if(langton.Canvas.ants.length < langton.Canvas.COLORS.length){
var ant =new langton.Ant();
ant.x=Math.floor(Math.random()*langton.Canvas.rowsize);
ant.y=Math.floor(Math.random()*langton.Canvas.rowsize);
ant.color=langton.Canvas.COLORS[langton.Canvas.ants.length];
var dirid = Math.floor(Math.random()*langton.Direction.CYCLE.length);
var dir = langton.Direction.CYCLE[dirid];
ant.direction=new langton.Direction(dir.x,dir.y);
langton.Canvas.ants.push(ant);
};
};
langton.Action=function(){
if(!langton.Canvas.body){
langton.Canvas.body=document.getElementById("canvas");
}
if(langton.Canvas.ants.length==0){
langton.Canvas.addAnt();
}
for(var i = 0; i < langton.Canvas.ants.length; i++){
langton.Canvas.ants[i].action(langton.Canvas.cells);
}
if(!langton.Canvas.counter){
langton.Canvas.counter=document.getElementById("counter");
}
var value = parseInt(langton.Canvas.counter.value,10)+1;
langton.Canvas.counter.value=value;
};
/* -------------------------------------------------------------------------------- */
langton.timer=0;
langton.isTimerOn=false;
langton.startTimer=function(){
langton.isTimerOn=true;
document.getElementById("run").disabled=true;
document.getElementById("clear").disabled=true;
langton.timer = setInterval(langton.executeTimer, 1);
};
langton.stopTimer=function(){
langton.isTimerOn=false;
};
langton.executeTimer=function(){
if(langton.isTimerOn) langton.Action();
else{
clearInterval(langton.timer);
document.getElementById("run").disabled=false;
document.getElementById("clear").disabled=false;
return 0;
}
};
function test(){
langton.Action();
}
// --></script>
<style type="text/css">
#canvas{
border: 1px solid #999;
width: 300px;
height: 300px;
background-color: black;
}
</style>
</head>
<body>
<p>
<input type="button"
value="run"
onclick="langton.startTimer();"
id="run" />
<input type="button"
value="add"
onclick="langton.Canvas.addAnt();" />
<input type="button"
value="stop"
onclick="langton.stopTimer();" />
<input type="button"
value="clear"
onclick="langton.Canvas.clear()"
id="clear" />
<input type="text" id="counter" disabled value=0 />
</p>
<div id="canvas" style="position:relative"></div>
</body>
</html>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | %!PS
gsave
0 dict begin
/fieldsize 100 def
/white [1 1 1] def
/field [ fieldsize{ [fieldsize{ white }repeat] }repeat ] def
/check-mark{
% ant -- bool
begin
field position-x get position-y get
color eq
end
}def
/draw-mark{
% ant --
begin
field position-x get position-y color put
end
}def
/erase-mark{
% ant --
begin
field position-x get position-y white put
end
}def
/generate-ant{
% init-x init-y direction-x direction-y color -- dict
5 dict begin
/color exch def
/direction-y exch def
/direction-x exch def
/position-y exch def
/position-x exch def
currentdict
end
}def
/move{
% ant --
begin
/position-x position-x direction-x add def
/position-y position-y direction-y add def
end
}def
/turn-left{
% ant --
begin
direction-y neg
direction-x
/direction-y exch def
/direction-x exch def
end
}def
/turn-right{
% ant --
begin
direction-y
direction-x neg
/direction-y exch def
/direction-x exch def
end
}def
/draw-field{
2 2 scale
0 1 fieldsize 1 sub{
% x
0 1 fieldsize 1 sub{
% x y
field 2 index get 1 index get
aload pop setrgbcolor
2 copy 1 1 rectfill
pop
}for
pop
}for
showpage
}def
/ant1
fieldsize 2 idiv dup 0 1 [0 0 0] generate-ant
def
0 1 20000{
ant1
dup check-mark{
dup erase-mark
dup turn-right
move
}{
dup draw-mark
dup turn-left
move
}ifelse
ant1 begin
position-x dup 0 lt exch fieldsize ge or
position-y dup 0 lt exch fieldsize ge or
or
end
{
exit
}if
% 20歩毎に表示
20 mod 0 eq{
draw-field
}if
}for
end
grestore
|
秀丸マクロで。 セルを全角で表現しているので、かなり幅が広くなってしまいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #w = 100;
#dct = 0;
$sp = " ";
$line = "";
#i = 0;
while (#i < #w) {
$line = $line+$sp;
#i = #i + 1;
}
$line = $line+"\n";
#i = 0;
while (#i < #w) {
insert $line;
#i = #i + 1;
}
movetolineno #w,#w/2;
#i = 0;
while (1) {
$ant = gettext(x, y, x+2, y);
if ($ant == $sp) {
overwrite "○";
#dct = (#dct+1)%4;
} else {
overwrite $sp;
#dct = (((#dct+1)%4)+2)%4;
}
left;
if (#dct == 0) {
up;
} else if (#dct == 1) {
right;
} else if (#dct == 2) {
down;
} else if (#dct == 3) {
left;
}
if ((x > #w*2-1)||(x == 0)||(y == 0)||(y > #w-1)) {
endmacro;
}
}
endmacro;
|
Tkで描画。 canvasを配置し、そこにドットを描く。 使うモジュールはTk, Tkclient。 メモ ・TKの使い方 ・初期値つきの二次元配列の定義 ・ドットの代わりにlineを使用している。メモリを沢山消費しているかも
see: A Descent into Limbo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | implement d276;
# doukaku?276 Langton
include "sys.m";
sys:Sys;
include "draw.m";
draw: Draw;
include "tk.m";
tk: Tk;
include "tkclient.m";
tkclient: Tkclient;
d276: module {
init: fn(ctxt: ref Draw->Context, argv: list of string);
};
init(ctxt: ref Draw->Context, argv: list of string)
{
sys = load Sys Sys->PATH;
sys->pctl(sys->NEWPGRP, nil);
draw = load Draw Draw->PATH;
tk = load Tk Tk->PATH;
tkclient = load Tkclient Tkclient->PATH;
# canvas size
height := 128;
width := 128;
pixel := array [width] of { * => array [height] of { * => 0}}; # 2dimensional array
curx := width/2;
cury := height/2;
dir := 0; # N: 0 E: 1 S: 2 W: 3
tkclient->init();
if(ctxt == nil)
ctxt = tkclient->makedrawcontext();
(win, wmctl) := tkclient->toplevel(ctxt, nil, "d276", 0);
# create canvas
tk->cmd(win, sys->sprint("canvas .b -width %d -height %d -background white",width , height));
tk->cmd(win, "pack .b");
tk->cmd(win, "update");
tkclient->onscreen(win, nil);
tkclient->startinput(win, "kbd"::"ptr"::nil);
for(;;){
# toggle pixel and turn
color :=pixel[curx][cury];
cstr : string;
if(color){
cstr = "white";
color = 0;
dir += 1;
if(dir > 3) dir = 0;
}else{
cstr = "black";
color = 1;
dir -= 1;
if(dir < 0) dir = 3;
}
pixel[curx][cury] = color;
# Plot (using line)
tk->cmd(win, sys->sprint(".b create line %d %d %d %d -fill %s", curx,cury, curx,cury,cstr));
tk->cmd(win, "update");
# update direction
case dir{
0 =>
cury--;
1 =>
curx++;
2 =>
cury++;
3 =>
curx--;
}
# if it reaches edge, it stop
if((curx < 0) || (curx >= width) || (cury < 0) || (cury >= height)){
break;
}
}
# Window event loop?
for(;;) alt{
s := <-win.ctxt.kbd =>
tk->keyboard(win, s);
s := <-win.ctxt.ptr =>
tk->pointer(win, *s);
s := <-win.ctxt.ctl or
s = <-win.wreq or
s = <-wmctl =>
if(s == "exit")
return;
}
}
|
アリの行動履歴を3次元表示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | world = zeros(100,100);
dirs = [[0,-1];[1,0];[0,1];[-1,0]];
posX = [60]; % X座標履歴
posY = [40]; % Y座標履歴
dir = 0;
while posX(1) > 0 & posX(1) <= 100 & posY(1) > 0 & posY(1) <= 100;
if world(posX(1),posY(1)) == 1
dir = mod(dir+1,4); % 右折
else
dir = mod(dir-1,4); % 左折
end;
world(posX(1),posY(1)) = abs(world(posX(1),posY(1))-1); % 白黒反転
posX = [[posX(1)+dirs(dir+1,1)],posX]; % X方向の移動
posY = [[posY(1)+dirs(dir+1,2)],posY]; % Y方向の移動
end;
plot3(posX,posY,length(posX):-1:1); % 軌跡表示 (Z軸:時間)
|
面白いですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | // 盤面の幅
WIDTH=100
HEIGHT=100
// 盤面の値
WHITE=0
BLACK=1
// 上下左右移動する際のリニア増減分
UP_DIFF=-WIDTH
RIGHT_DIFF=+1
DOWN_DIFF=+WIDTH
LEFT_DIFF=-1
// 上下左右移動に対応するリニア増減分を保存してある配列
direcVec=[UP_DIFF,RIGHT_DIFF,DOWN_DIFF,LEFT_DIFF]
// 上下左右の方向を表す定数値
UP=0
RIGHT=1
DOWN=2
LEFT=3
def move(x, y, dir) {
def pos = x + y*WIDTH
pos += direcVec[dir]
if (! (pos in (0 ..< WIDTH*HEIGHT))) {
throw new Exception("over")
}
x = pos % WIDTH
y = pos.intdiv(WIDTH)
return [x, y]
}
def reverse(x, y) {
def pos = x + y*WIDTH
bd[pos] = -(bd[pos]-1)
}
def color(x, y) {
def pos = x + y*WIDTH
bd[pos]
}
def turnRight(dir) {
(dir+1) % 4
}
def turnLeft(dir) {
(dir-1) % 4
}
def displayBoard() {
for (int y=0; y<HEIGHT; y++) {
for (int x=0; x<WIDTH; x++) {
print bd[x + y*WIDTH] == WHITE ? ' ' : '*'
}
println ""
}
}
def doit() {
def dir = RIGHT
while (true) {
def oldX=x
def oldY=y
if (color(x,y) == WHITE) { // 白なら右回転
dir = turnRight(dir)
}
else { // 白ではないなら左回転
dir = turnLeft(dir)
}
(x,y) = move(x,y,dir)
reverse(oldX, oldY)
}
}
// 盤面
bd = new int[WIDTH*HEIGHT]
// 画面の中央に配置
x = WIDTH.intdiv(2)
y = HEIGHT.intdiv(2)
try {
doit()
}
catch (Exception e) {
displayBoard()
}
|
コマンドライン上で描写するので、フィールドが狭いですがギリギリ列の生成は確認できました。列が生成されるまで時間がかかったので100回の移動ごとに描写していますが、この辺は適宜変更してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #include<stdio.h>
#include<stdlib.h>
#define XMAX 75 //フィールドの横幅
#define YMAX 75 //フィールドの縦幅
#define CNTSPACE 100 //描写間隔
char field[XMAX][YMAX];
enum Direction {up, right, down, left};
int main(void)
{
enum Direction dir;
int x, y, i, j, cnt = 0;
for(i = 0; i < XMAX; i++) //0が白、1が黒なので全て白に初期化
{
for(j = 0; j < YMAX; j++)
{
field[i][j] = 0;
}
}
x = XMAX / 2; //フィールド中央からスタート
y = YMAX / 2;
dir = up; //進行方向は上からスタート
while((x >= 0) && (y >= 0) && (x < XMAX) && (y < YMAX))
{
cnt++;
if(field[x][y])
{
field[x][y] = 0;
if(dir == left) dir = up;
else dir++;
}
else
{
field[x][y] = 1;
if(dir == up) dir = left;
else dir--;
}
switch(dir)
{
case up: y++; break;
case down: y--; break;
case right: x++; break;
case left: x--; break;
}
if(cnt == CNTSPACE)
{
system("cls");
for(i = 0; i < XMAX; i++)
{
for(j = 0; j < YMAX; j++)
{
field[i][j] ? putchar('@') : putchar('_');
}
putchar('\n');
}
cnt = 0;
}
}
return 0;
}
|
Scalaで。春っぽい雰囲気にしてみました!?(季節を無視して)。ScalaどころかSwingアプリとしても拙い作りかもしれません。。。 paintComponent内で結構ぐるぐる回してもいけるもんなんですね。もしかしたら、マシンパワーによっては表示が追いつかないのかもしれません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | import scala.collection.mutable.ListBuffer
import scala.concurrent.ops._
import scala.swing._
import scala.util._
import java.awt.{Graphics, Dimension}
trait AntWorld {
protected val w:Int
protected val h:Int
protected def reflect
var ants:List[Ant] = Nil
lazy val table:List[Array[Int]] = List.tabulate(w, {_=>Array.make(h,0)})
private val r = new Random()
def ant(c:Int) = new Ant(r.nextInt(w)/2+w/4, r.nextInt(h)/2+h/4, c)
def antStart() = {
spawn {
while(true) {
ants.foreach {_.move}
reflect
Thread.sleep(1)
}
}
}
class Ant(var x:Int, var y:Int, c:Int) {
private val rads = List((0,-1), (1,0), (0,1), (-1,0))
private val (initX, initY) = (x, y)
private var radi = 0
def init = { x = initX; y = initY; radi = 0 }
def move = {
val v = table(x)(y)
table(x)(y) = (if (v != 0) 0 else c)
radi = (radi + (if (v != 0) 1 else 3) ) % 4
x = (x + rads(radi)._1 + w) % w
y = (y + rads(radi)._2 + h) % h
}
}
}
object LangtonAnt extends SimpleGUIApplication {
import java.awt.Color
val aSize = 5
def top = new MainFrame {
title = "Langton's Ant"
val panel = new Panel() with AntWorld {
def reflect = repaint
val (w, h) = (100, 100)
peer.setPreferredSize(new Dimension(w * aSize, h * aSize))
override def paintComponent(g:Graphics) = {
super.paintComponent(g)
for (x <- 0 until w; y <- 0 until h) {
table(x)(y) match {
case 0 => g.setColor(Color.WHITE)
case c => g.setColor(new Color(c))
}
g.fillRect(x * aSize, y * aSize, aSize, aSize)
}
}
def clear = {
table.foreach { a => for (i <- 0 until a.length) a(i) = 0 }
}
def reset = {
ants =
ant(Color.GREEN.getRGB) ::
ant(Color.PINK.getRGB) ::
ant(Color.BLUE.getRGB) ::
ant(Color.RED.getRGB) ::
ant(Color.CYAN.getRGB) ::
Nil
clear
}
def replay = {
this.synchronized {
ants.foreach( _.init )
clear
}
}
reset
}
contents = new BoxPanel(Orientation.Vertical ) {
contents += new BoxPanel(Orientation.Horizontal ) {
contents += new Button(Action("Clear"){ panel.clear })
contents += new Button(Action("Replay"){ panel.replay })
contents += new Button(Action("Reset"){ panel.reset })
}
contents += panel
}
panel.antStart
}
}
|





Songmu #9331() [ JavaScript ] Rating8/10=0.80
- 黒いマスにアリがいた場合、90°右に方向転換し、そのマスの色を反転させ、1マス前進する。
- 白いマスにアリがいた場合、90°左に方向転換し、そのマスの色を反転させ、1マス前進する。
詳しくはWikipedia等で調べるか、参考ページに拙作のデモがありますのでご覧下さい。
see: JavaScriptでラングトンの蟻
Rating8/10=0.80-0+
[ reply ]