20200315のHTMLに関する記事は4件です。

学習記録(4日目)#相対パスから絶対パスを取得する方法

学習内容

  • 相対パスから絶対パスを取得する方法
  • Python基本文法

相対パスから絶対パスを取得する方法

HTMLでリンク先が相対パスで記述されていた際には、urllib.parse.urljon()を利用して絶対パスを取得する。

記述例
from urllib.parse import urljoin

base = "http://exsample.com/html/a.html"

compurl = lambda q: print(urljoin(base,q))
compurl("b.html")
compurl("sub/c.html")
compurl("../index.html")
compurl("../img/hoge.png")

実行結果

http://example.com/html/b.html
http://example.com/html/sub/c.html
http://example.com/index.html
http://example.com/img/hoge.png

Python基本文法

pass

Pythonではインデントによって処理ブロックを定義するので、処理を行う必要のない時には処理ブロック自体がなくなってしまう。そこで、何も処理を行うことを明示的に記述するためにpass文を用いる。

with open('exsample.txt', 'w'):
    pass

条件分岐

ifelifelseを用いて記述する。また、一つの条件式によって2通りの処理ブロックに分岐する場合は三項演算子を用いて記述することもできる。

if 条件式1:
    <処理ブロック1>
    # 条件式1がTrueのときに実行される処理
elif 条件式2:
    <処理ブロック2>
    # 条件式1がFalse、かつ条件式2がTrueのときに実行される処理
else:
    <処理ブロック3>
    # 条件式1がFalse、かつ条件式2がFalse、かつ条件式3がTrueのときに実行される処理

# 三項演算子による条件分岐
# 条件式がTrueのとき値1、Falseのとき値2
値1 if 条件式 else 値2

イテレータ

データを表す値そのものではなく、一連のデータを生成することができるルールとしてデータを保持する。大量のデータが必要なときには、イテレータを用いた方がメモリ効率が良い。以下にrange型のイテレータの記述例を示す。

# 以下3つのrange()は,0から9まで、1ずつ増えるイテレータを返す
range(0, 10, 1)
range(0, 10)
a = range(10)

print(a)
print(a[0])

実行結果

range(0, 10)
0

range()は、Pythonでは使うことができないインクリメント演算子(++)の機能を備えている。デクリメント(--)はreversed()というイテレータを用いて行う。

break文とcontinue

ループ文の処理ブロック内に記述することで、実行中のループ文を制御することができる。

break

現在実行中のループを中断してループの外に抜ける。

b = 0
while True:
    b += 1
    if b > 5
        break    # b = 6 となった時点でループから抜けて処理が終了する。
    print(b)

実行結果

1
2
3
4
5

continue

実行中の処理ブロックを中断して、ループの条件式評価まで処理を移動する。

c = 0
while True:
    c += 1
    if c < 5
        continue    # b = 6 となるまでこの先の処理は実行されない。
    print(c)
    break

実行結果

6

まとめ

利用しているスクレイピングに関する参考書のコードの理解に苦労し始めたので、再びPythonの基礎文法についての学習を始めることにした。CJavaでは非常に便利だったインクリメント・デクリメントが使えないことには驚いたが、累算代入(+=, -=)やイテレータを用いてうまく対応していけるようにしたい。

参考書

参考にした書籍から公開されているGitHubを添付しておきます。
増補改訂Pythonによるスクレイピング&機械学習 開発テクニック

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「感染を封じこめる」をJavaScriptとHTML(SVG, canvas)を使って可視化してみた

プログラマとして、なにかできることはないのか、、、。
リアルなウイルスに関して、無力感を感じていたところに、以下のエントリに出会った。

https://www.washingtonpost.com/graphics/2020/world/corona-simulator/

なるほど、こうやって可視化することで、パニックにならず冷静な対応をすることができるわけか、、、

このようなコードなら、プログラマには朝飯前? (これは、普段からあまり表には出てこない、プログラマという職業が、みなさまのお役にたてるチャンスかもしれないじゃないですか。)

そして、半日後にできたコードがこちらです。お納めください。
2020-03-16-2.gif

「感染を封じ込める」を可視化する

「START」ボタンを押すと、入力した条件で、どのくらい感染を封じ込められるのか(広がるのか)がシュミレートされます。
そして、いろいろ数値を変えて実行してみることで、どのパラメータが感染に効果があるのかもわかってきます。

See the Pen I tried to visualize the infection by yamazaki.3104 (@yamazaki3104) on CodePen.

初期条件では、おそらく収束するまでに 80% 近い感染者数になると思います。
トータルの感染者数を減らすには、

・移動速度を減らす → movespeed
・素早く治療する(もしくは閉じこもる) → cure_days
・接触しない(手を洗う、マスクをする?) → range

どれも効きますが、どれが効果があると思いますか?
ここに結論を書くよりも、みなさまで実際に試してみて、なにが効果的なのか各自で感じるとよいと思います。(わたしなりの結論は、このエントリの最後に書きます。)

また、コードもすべて載せておきますので、CodePenからでも、このページからでもご自由にご利用ください。ただし、このコードが正しく動作しているかどうかは、まったく保証できないし、計算された数値をどう解釈するかは各自の責任の範囲で使ってください

「こうしたほうがいいよ」とか「こういう条件も追加してシミュレートしてみたいのだけど、、、」といったコメントも歓迎しています。(ぶっちゃけ、プログラマは「すげー!」とか「ありがとう!」という言葉に飢えていると思います。)

全コード 174行

<html>
<body>
<script>
// このコードが正しく動作しているかどうかは、まったく保証できないし、計算された数値をどう解釈するかは各自の責任の範囲で使ってください

// Dashboard
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(b){var c=0;return function(){return c<b.length?{done:!1,value:b[c++]}:{done:!0}}};$jscomp.arrayIterator=function(b){return{next:$jscomp.arrayIteratorImpl(b)}};$jscomp.makeIterator=function(b){var c="undefined"!=typeof Symbol&&Symbol.iterator&&b[Symbol.iterator];return c?c.call(b):$jscomp.arrayIterator(b)};
var DbNode=function(b,c){this.con_text=this.elm_tag_name=null;this.elm_attribute={};this.elm_contents=[];this.self_node=null;this.parent_node=c;this.update(b)};DbNode.prototype.delete_node=function(){this.elm_contents=[];this.elm_attribute={};this.con_text=this.elm_tag_name=null;this.parent_node.removeChild(this.self_node)};
DbNode.prototype.update=function(b){if("[object Object]"==toString.call(b)){if(this.elm_tag_name!=b.tag_name){for(var c=$jscomp.makeIterator(this.elm_contents),d=c.next();!d.done;d=c.next())d.value.delete_node();null!=this.self_node&&this.parent_node.removeChild(this.self_node);""==b.tag_name?this.self_node=this.parent_node:("svg-"!=b.tag_name.slice(0,4)?this.self_node=document.createElement(b.tag_name):this.self_node=document.createElementNS("http://www.w3.org/2000/svg",b.tag_name.slice(4)),this.parent_node.appendChild(this.self_node));
this.elm_tag_name=b.tag_name;this.con_text=null;this.elm_attribute={};this.elm_contents=[]}for(var e in this.elm_attribute)e in b.attribute||this.self_node.removeAttribute(e);c={};for(var f in b.attribute)c.$jscomp$loop$prop$atr_key$2$7=f,"[object Function]"==toString.call(b.attribute[c.$jscomp$loop$prop$atr_key$2$7])?(b.self_node=this.self_node,this.self_node[c.$jscomp$loop$prop$atr_key$2$7]=function(c){return function(d){b.attribute[c.$jscomp$loop$prop$atr_key$2$7](b.self_data,d,b.self_node)}}(c)):
this.self_node.hasAttribute(c.$jscomp$loop$prop$atr_key$2$7)?this.self_node.setAttribute(c.$jscomp$loop$prop$atr_key$2$7,b.attribute[c.$jscomp$loop$prop$atr_key$2$7]):(d=document.createAttribute(c.$jscomp$loop$prop$atr_key$2$7),d.value=b.attribute[c.$jscomp$loop$prop$atr_key$2$7],this.self_node.setAttributeNode(d)),c={$jscomp$loop$prop$atr_key$2$7:c.$jscomp$loop$prop$atr_key$2$7};this.elm_attribute=b.attribute;if(this.elm_contents.length>b.contents.length)for(f=this.elm_contents.length-1;f>=b.contents.length;f--)this.elm_contents[f].delete_node(),
this.elm_contents.pop();for(var g in b.contents)g<this.elm_contents.length?this.elm_contents[g].update(b.contents[g]):this.elm_contents.push(new DbNode(b.contents[g],this.self_node))}else this.con_text!=b&&(null!=this.self_node&&this.parent_node.removeChild(this.self_node),this.self_node=document.createTextNode(b),this.parent_node.appendChild(this.self_node),this.elm_tag_name=null,this.con_text=b)};
var Dashboard={table:[],child:{},_UI_Ary_to_HTML_obj:function(b,c){var d={tag_name:"",attribute:{},contents:[],self_data:c};if("[object Array]"!=toString.call(b)||1>b.length)return d;if("[object String]"!=toString.call(b[0])){for(var e in b)d.contents.push(Dashboard._UI_Ary_to_HTML_obj(b[e],c));return d}d.tag_name=b[0];for(var f in b)if(0!=f){e=b[f];var g=toString.call(e);if("[object Object]"==g)for(var h in e)d.attribute[h]=e[h];else"[object Array]"==g?d.contents.push(Dashboard._UI_Ary_to_HTML_obj(e,
c)):d.contents.push(e)}return d},default_ui:function(b){var c=function(b){var d="",f=toString.call(b);if("[object Array]"==f)for(var g in b)d+='[ "div", { "style": "margin-left: 20px;" }, "'+g+': ", '+c(b[g])+" ], ";else if("[object Object]"==f)for(var h in b)d+='[ "div", { "style": "margin-left: 20px;" }, "'+h+': ", '+c(b[h])+" ], ";else d+='"'+b+'", ';return d.slice(0,-2)};return JSON.parse("[ "+c(b)+" ]")},bind:function(b){b.backup="";Dashboard.table.push(b);return b},_draw_animation_frame:function(){for(var b=
$jscomp.makeIterator(Dashboard.table),c=b.next();!c.done;c=b.next()){c=c.value;var d=JSON.stringify(c.data);if(c.backup!=d){c.backup=d;for(var e in c.view)if("[object Function]"==toString.call(c.view[e])&&(d=document.querySelector(e),null!=d)){var f=Dashboard._UI_Ary_to_HTML_obj(c.view[e](c.data),c.data);e in Dashboard.child?Dashboard.child[e].update(f):Dashboard.child[e]=new DbNode(f,d,c.data)}}}window.requestAnimationFrame(Dashboard._draw_animation_frame)}};window.requestAnimationFrame(Dashboard._draw_animation_frame);
</script>
<div id='div_ui'  class='Dashboard' ></div>
<div id='div_svg' class='Dashboard' ></div>
<div id='result'  class='Dashboard' ></div>
<canvas id='chart' width='555px' height='100px'></canvas>
<script>
class Point {
    constructor() {
        this.x = Math.random() * AREA_W
        this.y = Math.random() * AREA_H
        this.movespeed_x = (Math.random() - 0.5)
        this.movespeed_y = (Math.random() - 0.5)

        this.stat = 'nomal'
        this.elapsed_date = -1  // 経過日数
    }

    move() {    // 移動
        this.x += this.movespeed_x * ui.data.movespeed
        this.y += this.movespeed_y * ui.data.movespeed

        if ( this.x < 0 ) { this.x *= -1 ; this.movespeed_x *= -1 }
        if ( this.y < 0 ) { this.y *= -1 ; this.movespeed_y *= -1 }
        if ( this.x >= AREA_W ) { this.x = AREA_W - (AREA_W - this.x); this.movespeed_x *= -1 }
        if ( this.y >= AREA_H ) { this.y = AREA_H - (AREA_H - this.y); this.movespeed_y *= -1 }
    }

    check( _a ) {   // 確認
        if ( this.stat != 'have' ) return

        this.elapsed_date++ // 経過日数
        if ( this.elapsed_date > ui.data.complete_cure_days ) {
            this.stat = 'CompleteCure'
            return
        }

        const r = ui.data.range
        for ( const a of _a )
        {
            const rx = this.x - a.x
            if ( rx >  r ) continue
            if ( rx < -r ) continue
            const ry = this.y - a.y
            if ( ry >  r ) continue
            if ( ry < -r ) continue

            if ( rx * rx + ry * ry > r * r ) continue

            if ( a.stat == 'nomal' ) {
                a.stat = 'have'
                a.elapsed_date = 0
            }
        }
    }
}

const ui = Dashboard.bind(
{
    data: { people: 1000, movespeed: 10, complete_cure_days: 14, range: 10 },
    view: {
        'div#div_ui.Dashboard': ( _d ) => [
            [ 'div', 'people   : ', [ 'input', { type:'number', value: _d.people, oninput: (_dt,_ev,_el)=>{ _dt.people = _el.value } } ] ],
            [ 'div', 'movespeed: ', [ 'input', { type:'number', value: _d.movespeed, oninput: (_dt,_ev,_el)=>{ _dt.movespeed = _el.value } } ] ],
            [ 'div', 'cure_days: ', [ 'input', { type:'number', value: _d.complete_cure_days, oninput: (_dt,_ev,_el)=>{ _dt.complete_cure_days = _el.value } } ] ],
            [ 'div', 'range    : ', [ 'input', { type:'number', value: _d.range, oninput: (_dt,_ev,_el)=>{ _dt.range = _el.value } } ] ],
            [ 'button', 'START', { onclick: (_dt,_ev,_el)=>{ start_sim( _dt.people ) } } ],
        ],
    }
} )

const percent = (_p) => { return parseInt( _p / ui.data.people * 1000 ) / 10 }
const result = Dashboard.bind(
{
    data: { nomal_cnt: 0, have_cnt: 0, cure_cnt: 0, day: 0 },
    view: {
        'div#result.Dashboard': ( _d ) => [
            [ 'div', `nomal: ${ _d.nomal_cnt}, ${ percent(_d.nomal_cnt) }%` ],
            [ 'div', `have : ${ _d.have_cnt }, ${ percent(_d.have_cnt ) }%` ],
            [ 'div', `cure : ${ _d.cure_cnt }, ${ percent(_d.cure_cnt ) }%` ],
            [ 'div', `day  : ${ _d.day }` ],
        ]
    }
} )

const AREA_W = 555
const AREA_H = 333

const div_svg = Dashboard.bind(
{
    data: { p: [] },
    view: {
        'div#div_svg.Dashboard': (_d) => [
            [ 'svg-svg', { width: AREA_W+5*2, height: AREA_H+5*2, stroke: "#111", fill: "#ddd" },
                [ 'svg-rect',   { x: 0, y: 0, width: AREA_W+5*2, height: AREA_H+5*2, } ],
                _d.p.map( (_it)=>
                    [ 'svg-circle', {  cx: _it.x+5, cy: _it.y+5, r: '5px',
                     fill: _it.stat=='nomal'?'hsl(95, 46%, 75%)':_it.stat=='have'?'red':'hsl(300, 41%, 59%)' } ]
                )
            ]
        ]
    }
} )

const animation = () => {
    for ( let p of div_svg.data.p )
        p.move()

    for ( let p of div_svg.data.p )
        p.check( div_svg.data.p )

    let nomal_cnt = 0
    let have_cnt = 0
    let cure_cnt = 0
    for ( let p of div_svg.data.p )
    {
        if      ( p.stat == 'nomal' ) nomal_cnt ++
        else if ( p.stat == 'have'  ) have_cnt ++
        else                          cure_cnt ++
    }
    result.data.nomal_cnt = nomal_cnt
    result.data.have_cnt  = have_cnt
    result.data.cure_cnt  = cure_cnt

    // チャートに線を引く
    const ctx = document.querySelector('canvas#chart').getContext('2d')
    const draw_line = ( _x1, _y1, _x2, _y2, _color ) => {
        ctx.beginPath()
        ctx.moveTo( _x1, _y1 )
        ctx.lineTo( _x2, _y2 )
        ctx.moveTo( _x1-1, _y1 )
        ctx.lineTo( _x2-1, _y2 )
        ctx.strokeStyle = _color
        ctx.stroke()
    }
    const n = percent( nomal_cnt )
    const h = percent( have_cnt )
    draw_line( result.data.day+1, 0, result.data.day+1, 100, '#fff' )
    draw_line( result.data.day, 0, result.data.day, n, 'hsl(95, 46%, 75%)' )
    draw_line( result.data.day, n, result.data.day, n+h, 'red' )
    draw_line( result.data.day, n+h, result.data.day, 100, 'hsl(300, 41%, 59%)' )
    result.data.day++
    if ( result.data.day > 555 ) result.data.day = 0

    if ( have_cnt > 0 )
        window.setTimeout( animation, 1000/20 ) // 20fps
}

const start_sim = () => {
    div_svg.data.p = []
    for ( let i=0 ; i<ui.data.people ; ++i )
        div_svg.data.p.push( new Point() )

    div_svg.data.p[0].stat = 'have'
    result.data.day = 0
    window.setTimeout( animation, 1000/20 ) // 20fps
}
</script>
</body>
</html>

細かい解説をしなくても、緑の丸がワシャワシャ動いているメインのところを SVG で描いていて、下のチャートの部分は canvas で描いています。どちらも一長一短がありますので、適材適所で使い分けています。

いまのところ、Chrome で動作の確認を行っています。

わたしの考察を書くと
「感染には移動が効く。とにかく一定時間じっとしているといい。ぶっちゃけ感染期間に何人と接触したのか?で決まる」
です。
このコードでは、movespeed が3以下になれば、ほどんと感染が広がらないことが確認できます。

もしくは、以下の2点で感染者数が劇的に変化することがわかると思います。お試しあれ。

movespeed: 5 ← 出歩かない(移動を半分にする)
cure_days: 14
range : 5 ← 感染距離を半分にする(手洗いをする)

みなさま、ふんばりどころです。がんばりましょう。

このコードが少しでもみなさまのお役にたてば幸いです。

Dashboard.js

コードの先頭に minify されているコードは、Dashboard と名付けた UI ライブラリですが、まだ、テストも不十分なので、しばらく minify のままとさせてください。みなさまからの声が集まったら、しっかりテストしたのちに公開、、、するかもしれないです。ってか、UIのテストって、どうやって書くんでしょう??
Dashboard.js に関してもコメント募集中です。よろしくお願いいたします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript と HTML を使って「感染を封じこめる」を可視化してみた

プログラマとして、なにかできることはないのか、、、。
リアルなウイルスに関して、無力感を感じていたところに、以下のエントリに出会った。

https://www.washingtonpost.com/graphics/2020/world/corona-simulator/

なるほど、こうやって可視化することで、パニックにならず冷静な対応をすることができるわけか、、、

このようなコードなら、プログラマには朝飯前? (これは、普段からあまり表には出てこない、プログラマという職業が、みなさまのお役にたてるチャンスかもしれないじゃないですか。)

そして、半日後にできたコードがこちらです。お納めください。
2020-03-16-2.gif

「感染を封じ込める」を可視化する

「START」ボタンを押すと、入力した条件で、どのくらい感染を封じ込められるのか(広がるのか)がシュミレートされます。
そして、いろいろ数値を変えて実行してみることで、どのパラメータが感染に効果があるのかもわかってきます。

See the Pen I tried to visualize the infection by yamazaki.3104 (@yamazaki3104) on CodePen.

初期条件では、おそらく収束するまでに 80% 近い感染者数になると思います。
トータルの感染者数を減らすには、

・移動速度を減らす → movespeed
・素早く治療する(もしくは閉じこもる) → cure_days
・接触しない(手を洗う、マスクをする?) → range

どれも効きますが、どれが効果があると思いますか?
ここに結論を書くよりも、みなさまで実際に試してみて、なにが効果的なのか各自で感じるとよいと思います。(わたしなりの結論は、このエントリの最後に書きます。)

また、コードもすべて載せておきますので、CodePenからでも、このページからでもご自由にご利用ください。ただし、このコードが正しく動作しているかどうかは、まったく保証できないし、計算された数値をどう解釈するかは各自の責任の範囲で使ってください

「こうしたほうがいいよ」といったコメントも募集しています。

全コード 174行

<html>
<body>
<script>
// このコードが正しく動作しているかどうかは、まったく保証できないし、計算された数値をどう解釈するかは各自の責任の範囲で使ってください

// Dashboard
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(b){var c=0;return function(){return c<b.length?{done:!1,value:b[c++]}:{done:!0}}};$jscomp.arrayIterator=function(b){return{next:$jscomp.arrayIteratorImpl(b)}};$jscomp.makeIterator=function(b){var c="undefined"!=typeof Symbol&&Symbol.iterator&&b[Symbol.iterator];return c?c.call(b):$jscomp.arrayIterator(b)};
var DbNode=function(b,c){this.con_text=this.elm_tag_name=null;this.elm_attribute={};this.elm_contents=[];this.self_node=null;this.parent_node=c;this.update(b)};DbNode.prototype.delete_node=function(){this.elm_contents=[];this.elm_attribute={};this.con_text=this.elm_tag_name=null;this.parent_node.removeChild(this.self_node)};
DbNode.prototype.update=function(b){if("[object Object]"==toString.call(b)){if(this.elm_tag_name!=b.tag_name){for(var c=$jscomp.makeIterator(this.elm_contents),d=c.next();!d.done;d=c.next())d.value.delete_node();null!=this.self_node&&this.parent_node.removeChild(this.self_node);""==b.tag_name?this.self_node=this.parent_node:("svg-"!=b.tag_name.slice(0,4)?this.self_node=document.createElement(b.tag_name):this.self_node=document.createElementNS("http://www.w3.org/2000/svg",b.tag_name.slice(4)),this.parent_node.appendChild(this.self_node));
this.elm_tag_name=b.tag_name;this.con_text=null;this.elm_attribute={};this.elm_contents=[]}for(var e in this.elm_attribute)e in b.attribute||this.self_node.removeAttribute(e);c={};for(var f in b.attribute)c.$jscomp$loop$prop$atr_key$2$7=f,"[object Function]"==toString.call(b.attribute[c.$jscomp$loop$prop$atr_key$2$7])?(b.self_node=this.self_node,this.self_node[c.$jscomp$loop$prop$atr_key$2$7]=function(c){return function(d){b.attribute[c.$jscomp$loop$prop$atr_key$2$7](b.self_data,d,b.self_node)}}(c)):
this.self_node.hasAttribute(c.$jscomp$loop$prop$atr_key$2$7)?this.self_node.setAttribute(c.$jscomp$loop$prop$atr_key$2$7,b.attribute[c.$jscomp$loop$prop$atr_key$2$7]):(d=document.createAttribute(c.$jscomp$loop$prop$atr_key$2$7),d.value=b.attribute[c.$jscomp$loop$prop$atr_key$2$7],this.self_node.setAttributeNode(d)),c={$jscomp$loop$prop$atr_key$2$7:c.$jscomp$loop$prop$atr_key$2$7};this.elm_attribute=b.attribute;if(this.elm_contents.length>b.contents.length)for(f=this.elm_contents.length-1;f>=b.contents.length;f--)this.elm_contents[f].delete_node(),
this.elm_contents.pop();for(var g in b.contents)g<this.elm_contents.length?this.elm_contents[g].update(b.contents[g]):this.elm_contents.push(new DbNode(b.contents[g],this.self_node))}else this.con_text!=b&&(null!=this.self_node&&this.parent_node.removeChild(this.self_node),this.self_node=document.createTextNode(b),this.parent_node.appendChild(this.self_node),this.elm_tag_name=null,this.con_text=b)};
var Dashboard={table:[],child:{},_UI_Ary_to_HTML_obj:function(b,c){var d={tag_name:"",attribute:{},contents:[],self_data:c};if("[object Array]"!=toString.call(b)||1>b.length)return d;if("[object String]"!=toString.call(b[0])){for(var e in b)d.contents.push(Dashboard._UI_Ary_to_HTML_obj(b[e],c));return d}d.tag_name=b[0];for(var f in b)if(0!=f){e=b[f];var g=toString.call(e);if("[object Object]"==g)for(var h in e)d.attribute[h]=e[h];else"[object Array]"==g?d.contents.push(Dashboard._UI_Ary_to_HTML_obj(e,
c)):d.contents.push(e)}return d},default_ui:function(b){var c=function(b){var d="",f=toString.call(b);if("[object Array]"==f)for(var g in b)d+='[ "div", { "style": "margin-left: 20px;" }, "'+g+': ", '+c(b[g])+" ], ";else if("[object Object]"==f)for(var h in b)d+='[ "div", { "style": "margin-left: 20px;" }, "'+h+': ", '+c(b[h])+" ], ";else d+='"'+b+'", ';return d.slice(0,-2)};return JSON.parse("[ "+c(b)+" ]")},bind:function(b){b.backup="";Dashboard.table.push(b);return b},_draw_animation_frame:function(){for(var b=
$jscomp.makeIterator(Dashboard.table),c=b.next();!c.done;c=b.next()){c=c.value;var d=JSON.stringify(c.data);if(c.backup!=d){c.backup=d;for(var e in c.view)if("[object Function]"==toString.call(c.view[e])&&(d=document.querySelector(e),null!=d)){var f=Dashboard._UI_Ary_to_HTML_obj(c.view[e](c.data),c.data);e in Dashboard.child?Dashboard.child[e].update(f):Dashboard.child[e]=new DbNode(f,d,c.data)}}}window.requestAnimationFrame(Dashboard._draw_animation_frame)}};window.requestAnimationFrame(Dashboard._draw_animation_frame);
</script>
<div id='div_ui'  class='Dashboard' ></div>
<div id='div_svg' class='Dashboard' ></div>
<div id='result'  class='Dashboard' ></div>
<canvas id='chart' width='555px' height='100px'></canvas>
<script>
class Point {
    constructor() {
        this.x = Math.random() * AREA_W
        this.y = Math.random() * AREA_H
        this.movespeed_x = (Math.random() - 0.5)
        this.movespeed_y = (Math.random() - 0.5)

        this.stat = 'nomal'
        this.elapsed_date = -1  // 経過日数
    }

    move() {    // 移動
        this.x += this.movespeed_x * ui.data.movespeed
        this.y += this.movespeed_y * ui.data.movespeed

        if ( this.x < 0 ) { this.x *= -1 ; this.movespeed_x *= -1 }
        if ( this.y < 0 ) { this.y *= -1 ; this.movespeed_y *= -1 }
        if ( this.x >= AREA_W ) { this.x = AREA_W - (AREA_W - this.x); this.movespeed_x *= -1 }
        if ( this.y >= AREA_H ) { this.y = AREA_H - (AREA_H - this.y); this.movespeed_y *= -1 }
    }

    check( _a ) {   // 確認
        if ( this.stat != 'have' ) return

        this.elapsed_date++ // 経過日数
        if ( this.elapsed_date > ui.data.complete_cure_days ) {
            this.stat = 'CompleteCure'
            return
        }

        const r = ui.data.range
        for ( const a of _a )
        {
            const rx = this.x - a.x
            if ( rx >  r ) continue
            if ( rx < -r ) continue
            const ry = this.y - a.y
            if ( ry >  r ) continue
            if ( ry < -r ) continue

            if ( rx * rx + ry * ry > r * r ) continue

            if ( a.stat == 'nomal' ) {
                a.stat = 'have'
                a.elapsed_date = 0
            }
        }
    }
}

const ui = Dashboard.bind(
{
    data: { people: 1000, movespeed: 10, complete_cure_days: 14, range: 10 },
    view: {
        'div#div_ui.Dashboard': ( _d ) => [
            [ 'div', 'people   : ', [ 'input', { type:'number', value: _d.people, oninput: (_dt,_ev,_el)=>{ _dt.people = _el.value } } ] ],
            [ 'div', 'movespeed: ', [ 'input', { type:'number', value: _d.movespeed, oninput: (_dt,_ev,_el)=>{ _dt.movespeed = _el.value } } ] ],
            [ 'div', 'cure_days: ', [ 'input', { type:'number', value: _d.complete_cure_days, oninput: (_dt,_ev,_el)=>{ _dt.complete_cure_days = _el.value } } ] ],
            [ 'div', 'range    : ', [ 'input', { type:'number', value: _d.range, oninput: (_dt,_ev,_el)=>{ _dt.range = _el.value } } ] ],
            [ 'button', 'START', { onclick: (_dt,_ev,_el)=>{ start_sim( _dt.people ) } } ],
        ],
    }
} )

const percent = (_p) => { return parseInt( _p / ui.data.people * 1000 ) / 10 }
const result = Dashboard.bind(
{
    data: { nomal_cnt: 0, have_cnt: 0, cure_cnt: 0, day: 0 },
    view: {
        'div#result.Dashboard': ( _d ) => [
            [ 'div', `nomal: ${ _d.nomal_cnt}, ${ percent(_d.nomal_cnt) }%` ],
            [ 'div', `have : ${ _d.have_cnt }, ${ percent(_d.have_cnt ) }%` ],
            [ 'div', `cure : ${ _d.cure_cnt }, ${ percent(_d.cure_cnt ) }%` ],
            [ 'div', `day  : ${ _d.day }` ],
        ]
    }
} )

const AREA_W = 555
const AREA_H = 333

const div_svg = Dashboard.bind(
{
    data: { p: [] },
    view: {
        'div#div_svg.Dashboard': (_d) => [
            [ 'svg-svg', { width: AREA_W+5*2, height: AREA_H+5*2, stroke: "#111", fill: "#ddd" },
                [ 'svg-rect',   { x: 0, y: 0, width: AREA_W+5*2, height: AREA_H+5*2, } ],
                _d.p.map( (_it)=>
                    [ 'svg-circle', {  cx: _it.x+5, cy: _it.y+5, r: '5px',
                     fill: _it.stat=='nomal'?'hsl(95, 46%, 75%)':_it.stat=='have'?'red':'hsl(300, 41%, 59%)' } ]
                )
            ]
        ]
    }
} )

const animation = () => {
    for ( let p of div_svg.data.p )
        p.move()

    for ( let p of div_svg.data.p )
        p.check( div_svg.data.p )

    let nomal_cnt = 0
    let have_cnt = 0
    let cure_cnt = 0
    for ( let p of div_svg.data.p )
    {
        if      ( p.stat == 'nomal' ) nomal_cnt ++
        else if ( p.stat == 'have'  ) have_cnt ++
        else                          cure_cnt ++
    }
    result.data.nomal_cnt = nomal_cnt
    result.data.have_cnt  = have_cnt
    result.data.cure_cnt  = cure_cnt

    // チャートに線を引く
    const ctx = document.querySelector('canvas#chart').getContext('2d')
    const draw_line = ( _x1, _y1, _x2, _y2, _color ) => {
        ctx.beginPath()
        ctx.moveTo( _x1, _y1 )
        ctx.lineTo( _x2, _y2 )
        ctx.moveTo( _x1-1, _y1 )
        ctx.lineTo( _x2-1, _y2 )
        ctx.strokeStyle = _color
        ctx.stroke()
    }
    const n = percent( nomal_cnt )
    const h = percent( have_cnt )
    draw_line( result.data.day+1, 0, result.data.day+1, 100, '#fff' )
    draw_line( result.data.day, 0, result.data.day, n, 'hsl(95, 46%, 75%)' )
    draw_line( result.data.day, n, result.data.day, n+h, 'red' )
    draw_line( result.data.day, n+h, result.data.day, 100, 'hsl(300, 41%, 59%)' )
    result.data.day++
    if ( result.data.day > 555 ) result.data.day = 0

    if ( have_cnt > 0 )
        window.setTimeout( animation, 1000/20 ) // 20fps
}

const start_sim = () => {
    div_svg.data.p = []
    for ( let i=0 ; i<ui.data.people ; ++i )
        div_svg.data.p.push( new Point() )

    div_svg.data.p[0].stat = 'have'
    result.data.day = 0
    window.setTimeout( animation, 1000/20 ) // 20fps
}
</script>
</body>
</html>

いまのところ、Chrome で動作の確認を行っています。

わたしの考察は、感染には、移動が効く。とにかく一定時間じっとしているといい。
このコードでは、movespeed が3以下になれば、ほどんと感染が広がらないことが確認できます。

もしくは、以下の2点で感染者数が劇的に変化することがわかると思います。お試しあれ。

movespeed: 5 ← 出歩かない(移動を半分にする)
cure_days: 14
range : 5 ← 感染距離を半分にする(手洗いをする)

みなさま、ふんばりどころです。がんばりましょう。

このコードが少しでもみなさまのお役にたてば幸いです。

Dashboard.js

コードの先頭に minify されているコードは、Dashboard と名付けた UI ライブラリですが、まだ、テストも不十分なので、しばらく minify のままとさせてください。みなさまからの声が集まったら、しっかりテストしたのちに公開、、、するかもしれないです。ってか、UIのテストって、どうやって書くんでしょう??
Dashboard.js に関してもコメント募集中です。よろしくお願いいたします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者によるプログラミング学習ログ 260~262日目

100日チャレンジの260~262日目

twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。
100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
260~262日目は、

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む