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 - C#

C#でビットマップにごりごり描画します。 フレームスキップして速度を稼いでいます。

今回一番はまったのは、Color.FromArgb(255, 0, 0, 0) が Color.Black とイコールではないってこと。ColorはARGB以外の情報を持っているので、ToArgb()で比較するように、とMSDNにある。

  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());
        }
    }
}

アリ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);
            }
        }
    }
}

Index

Feed

Other

Link

Pathtraq

loading...