challenge ラングトンのアリの描画

ラングトンのアリを描画してください。ラングトンのアリは、以下のような動きをする、セル・オートマトンです。(Wikipediaより引用)
- 黒いマスにアリがいた場合、90°右に方向転換し、そのマスの色を反転させ、1マス前進する。
- 白いマスにアリがいた場合、90°左に方向転換し、そのマスの色を反転させ、1マス前進する。
詳しくはWikipedia等で調べるか、参考ページに拙作のデモがありますのでご覧下さい。
  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
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="ja">
<head>
       <meta http-equiv="content-type" content="text/html;charset=utf-8">
       <meta http-equiv="content-script-type" content="text/javascript">
       <meta http-equiv="content-style-type" content="text/css">
       <title>ラングトンの蟻</title>
<style type="text/css">
#canvas{
       border: 1px solid #999;
       width: 300px;
       height: 300px;
}
#canvas div{
       width: 3px;
       height: 3px;
       float: left;
}
</style>
<script>
var earth = [];
var WORLD_SIZE = 100;
var lang_ant;

function Ant(){}
Ant.prototype = {
    age : 0,
    ageDisplay : undefined,
    id : undefined,
    speed: 200,
    direction: [0,-1],//向き。x y軸。最初は上に動く
    position: [60,40],//初期位置
    world: [],
    start: function(){
        var self = this;
        this.id = setInterval(function(){self.move()}, 1000/this.speed);
    },
    move: function(){
        this.ageDisplay.innerHTML = ++this.age;
        this.moveNextCell();
        var cell = this.getCellInfo();
        var color = cell.getAndToggleColor();
        this.setNextDirection(color);
    },
    moveNextCell: function(){
        this.position[0] += this.direction[0];
        this.position[1] += this.direction[1];
        if(this.position[0] < 0 || this.position[1] < 0 ||
           this.position[0] >= WORLD_SIZE || this.position[1] >= WORLD_SIZE){
            clearInterval(this.id);
            this.die();
        }
    },
    getCellInfo: function(){
        var idx = this.position[0] + this.position[1] * WORLD_SIZE;
        return this.world[idx];
    },
    setNextDirection: function(bool){//colorがfalse(白)なら右へ、true(黒)なら左へ転回
        if(bool){//黒
            var tmp = this.direction[0];
            this.direction[0] = this.direction[1];
            this.direction[1] = -tmp;
        }
        else{//白
            var tmp = this.direction[0];
            this.direction[0] = -this.direction[1];
            this.direction[1] = tmp;
        }
    },
    die: function(){
        alert('Langton\'s ant is dead.');
        throw true;
    }
};

function Cell(elm){
    this.elm = elm;
}
Cell.prototype = {
    elm: undefined,
    color: false, //colorは2値なのでbooleanで表す
    colorList: ['#FFF','#000'],
    getAndToggleColor: function(){
        this.color = !this.color;
        var i = this.color ? 1 : 0;
        this.elm.style.backgroundColor = this.colorList[i];
        return !this.color;
    }
}
window.onload = function(){
    var canvas = document.getElementById('canvas');
    var div = '<div></div>';
    var inner_canvas = "";
    for(var i=0; i< WORLD_SIZE*WORLD_SIZE; i++){
        inner_canvas += div;
    }
    canvas.innerHTML = inner_canvas;
    
    var cells = canvas.childNodes;
    for(var i=0; i<cells.length; i++){//世界の誕生
        earth[i] = new Cell(cells[i]);
    }
    lang_ant = new Ant();//蟻の誕生
    lang_ant.world = earth;//地球に降り立つ
    lang_ant.ageDisplay = document.getElementById('step');
    
    document.getElementById('run').disabled = false;
}
</script>
</head>
<body>
<p><input type="button" value="run" onclick="lang_ant.start();this.disabled=true;" id="run" disabled="disabled"> <span id="step"></span>
 <input type="button" value="stop &amp; refresh" onclick="location.reload();"></p>
<div id="canvas"></div>

Posted feedbacks - Other

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

Tkで描画。
canvasを配置し、そこにドットを描く。
使うモジュールはTk, Tkclient。

メモ
・TKの使い方
・初期値つきの二次元配列の定義
・ドットの代わりにlineを使用している。メモリを沢山消費しているかも
  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;
    }
}

Index

Feed

Other

Link

Pathtraq

loading...