[JS]HTML5 Canvasを使ったライフゲーム

HTML5 で導入された要素 Canvas には自由に図形を描画でき、JavaScript と組み合わせることでリアルタイムに書き換えることができます。
そこで練習も兼ねて HTML5 + JavaScript でシンプルなライフゲームを作ってみました。

lifegame

Demo

・html 部分

<!DOCTYPE html>
<html>
<head>
<title>Game of Life</title>
<meta charset="utf-8">
<script src="lifegame.js"></script>
</head>
<body>

<div style="margin: 1em 0;">
<form>
<input type="button" value="Random" id="buttonRandom">
<input type="button" value="Start" id="buttonStart">
<input type="button" value="Reset" id="buttonReset">
</form>
</div>

<canvas id="lifegame" width="320" height="320"></canvas>
</body>
</html>

・スクリプト部分(lifegame.js)

var canvas;
var ctx;
var cellSize = 8;	// セル1マスのサイズ
var cols;
var rows;
var cells = new Array();
var buttonStart;
var buttonRandom;
var buttonReset;
var timer1;
var running = false;

window.onload = function()
{
	canvas = document.getElementById('lifegame');
	ctx = canvas.getContext('2d');
	cols = Math.floor(canvas.width / cellSize);
	rows = Math.floor(canvas.height / cellSize);
	initCells();
	buttonStart = document.getElementById('buttonStart');
	buttonRandom = document.getElementById('buttonRandom');
	buttonReset = document.getElementById('buttonReset');
	buttonStart.addEventListener('click', onStart, false);
	buttonRandom.addEventListener('click', randomCells, false);
	buttonReset.addEventListener('click', initCells, false);
	canvas.addEventListener('click', canvasClick, false);
};

// 開始
function onStart(){
	if(running){
		clearInterval(timer1);
		buttonStart.value = "Start";
		running = false;
	} else {
		nextGeneration();
		timer1 = setInterval("nextGeneration()", 100);
		buttonStart.value = "Stop";
		running = true;
	}
}

// 初期化
function initCells(){
	ctx.fillStyle = 'rgb(60, 60, 60)';
	ctx.fillRect(0,0, canvas.width, canvas.height);
	for(col=0;col<cols;col++){
		cells[col] = new Array();
		for(row=0;row<rows;row++){
			cells[col][row] = 0;
		}
	}
	redraw();
}

// ランダムに埋める
function randomCells(){
	for(col=0;col<cols;col++){
		cells[col] = new Array();
		for(row=0;row<rows;row++){
			cells[col][row] = Math.round( Math.random());
		}
	}
	redraw();
}

// 全体を再描画
function redraw(){
	for(col=0;col<cols;col++){
		for(row=0;row<rows;row++){
			drawCell(col, row);
		}
	}
}

// セルを描画
function drawCell(x, y){
	var value = cells[x][y];
	var style = value ? "rgb(156, 255,0)" : "rgb(40,40,40)"; 
    ctx.fillStyle = style;
	ctx.fillRect(x * cellSize, y * cellSize,
		cellSize - 1, cellSize - 1);
}

// 世代を進行させる
function nextGeneration(){
	var tmpCells = new Array();
	for(col=0;col<cols;col++){
		tmpCells[col] = new Array();
		for(row=0;row<rows;row++){
			var count = countAround(col, row);
			if(cells[col][row]){
				if(count == 2 || count == 3){
					tmpCells[col][row] = 1;
				} else {
					tmpCells[col][row] = 0;
				}
			} else {
				if(count == 3){
					tmpCells[col][row] = 1;
				} else {
					tmpCells[col][row] = 0;
				}
			}
		}
	}
	cells = tmpCells;
	redraw();
}

// 周囲の生存セルを数える
function countAround(x, y){
	var count = 0;
	for(i=-1;i<=1;i++){
		for(j=-1;j<=1;j++){
			if(
				(i != 0 || j != 0) &&
				x + i >= 0 && x + i < cols &&
				y + j >= 0 && y + j < rows
			) {
				count += cells[x + i][y + j];
			}
		}
	}
	return count;
}

// Canvasクリック
function canvasClick(e){
    var x = e.clientX - canvas.offsetLeft;
    var y = e.clientY - canvas.offsetTop;
	var col = Math.floor(x / cellSize);
	var row = Math.floor(y / cellSize);
	cells[col][row] = !cells[col][row];
	drawCell(col, row);
}

ランダムボタンでランダムにセルを埋めるかマウスクリックでセルの ON/OFF を設定してスタートボタンで開始します。
セルの周囲に2~3の生存セルが隣接している場合生き残り、過疎(1以下)や過密(4以上)の場合消滅します。
死んでいるセルの周囲にちょうど3つの生存セルが隣接している場合生きているセルが発生します。

[JS]Javascriptを使ったタイピングゲーム

練習のために JavaScript を使った簡易タイピングゲームを作ってみました。
単語一覧の中からランダムに出題され、制限時間以内にいくつ入力できたかを計測します。

DEMO

<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script>
// 単語
var wordList = [
	"Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India",
	"Juliett", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo",
	"Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu"
];

// 時間制限
var timeLimit = 30;

var timer1;
var wordStr;
var wordChars;
var charIndex;
var messageArea;
var wordArea;
var typeArea;
var score;
var timeLeft;

window.onload = function (){
	messageArea = document.getElementById("message");
	wordArea    = document.getElementById("word");
	typeArea    = document.getElementById("type");
	startButton = document.getElementById("start-button");
}

// 3秒後に開始
function onStartButtonClick(){
	messageArea.textContent = "Ready?";
	setTimeout("startTyping()", 3000);
}

// 開始
function startTyping(){
	score = 0;
	timeLeft = timeLimit;
	nextWord();
	countDown();
	timer1 = setInterval("countDown()", 1000);
	startButton.disabled = true;
}

// 終了
function stopTyping(){
	clearInterval(timer1);
	wordChars = [];
	messageArea.textContent = "Score: " + score;
	wordArea.textContent = "";
	typeArea.textContent = "";
	startButton.disabled = false;
}

// 次の単語を表示
function nextWord(){
	charIndex = 0;
	var random = Math.floor( Math.random() * wordList.length );
	wordArea.textContent = wordList[random];
	typeArea.textContent = "";
	wordChars = wordList[random].toUpperCase().split('');
}

// 残り時間を計測
function countDown(){
	if(timeLeft <= 0) {
		stopTyping();
		return;
	}
	messageArea.textContent = timeLeft + " sec.";
	timeLeft--;
}

// キー押下時の処理
document.onkeydown = function (e){
	var keyStr;
	if(e.keyCode == 189){
		keyStr = "-";
	} else {
		keyStr = String.fromCharCode(e.keyCode);
	}
	
	if(wordChars[charIndex] == keyStr){
		charIndex++;
		typeArea.textContent = typeArea.textContent + keyStr;
		if(charIndex== wordChars.length){
			score++;
			nextWord();
		}
	}
};
</script>
</head>
<body>

<form><p><input type="button" id="start-button" value="Start" onClick="onStartButtonClick()">
<span id="message">Press Start</span></p></form>

<hr>

<div style="font-weight: bold;" id="word">&nbsp;</div>
<div id="type">&nbsp;</div>

</body>
</html>

単語を split() で一文字ごとに区切り、入力した文字と先頭の文字が一致すれば一文字進めます。同様にその文字と一致すれば更に一文字進め、最後まで到達したら別の単語を表示します。

タイマーで残り時間を計測し、時間が来たらスコアを表示して終わります。

[PHP]WordPress用のサイトマップを独自に動的生成

WordPress のサイトマップは通常プラグインを使って作るのが一般的ですが、
条件を自由に設定する必要があったり、WordPress をサブディレクトリにインストールしている場合や、
好きな階層にサイトマップを置きたい場合、プラグインだけでは実現できないことが有ります。

サイトマップの生成には以前作った SitemapGenerator クラスを使用します。
まずはそちらを参考に sitemap_generator.php を用意して下さい。

<?php
// wp-load.php を読み込む(環境に応じて書き換えて下さい)
require_once __DIR__ . '/../wordpress/wp-load.php';

require_once __DIR__ . '/sitemap_generator.php';

$sitemap = new SitemapGenerator();

// 取得する記事の設定
$args = array(
	'posts_per_page'   => -1, // すべての記事
	'offset'           => 0,
	'category_name'    => '',
	'orderby'          => 'post_date', // 投稿順
	'order'            => 'DESC',
	'include'          => '',
	'exclude'          => '',
	'meta_key'         => '',
	'meta_value'       => '',
	'post_type'        => ['post', 'page'],	// ブログ記事と固定ページ
	'post_mime_type'   => '',
	'post_parent'      => '',
	'post_status'      => 'publish', // 公開されている記事のみ
	'suppress_filters' => false
);

// WordPress ホームページを登録
$datetime1 = new DateTime(get_lastpostmodified());
$home = array(
	'loc' => home_url(),
	'last_mod' => $datetime1->format('c'),
	'changefreq' => 'daily',
	'priority' => 1.0
);
$sitemap->add($home);

// 投稿と固定ページを登録
$posts = get_posts( $args );

foreach($posts as $post){
    $permalink = get_permalink($post->ID);
    if($permalink === false) $permalink = $post->guid;
    $datetime2 = new DateTime($post->post_modified);
    $param = array(
        'loc' => $permalink,
        'lastmod' => $datetime2->format('c'),
        'changefreq' => 'monthly',
        'priority' => 0.5
    );
    $sitemap->add($param);
}

$sitemap->generate();
exit;

「wp-load.php」は WordPress のフォルダ内にあるファイルで、これを読み込むと WordPress の関数が利用できるようになります。
設置場所の環境に合わせて正しいファイルパスに書き換えて下さい。

「http://example.com/sitemap/index.php」のように配置しておけば、「http://example.com/sitemap」にアクセスした時、自動的にサイトマップが生成され、XML として表示されます。
もしアクセスが頻繁で負荷が高いようなら generate() にファイルパスを渡して sitemap.xml としてファイルに保存しておくのも良いと思います。