Skip to content

Instantly share code, notes, and snippets.

@xhackjp1
Last active January 22, 2021 01:24
Show Gist options
  • Save xhackjp1/5ffd4641889d5eb28bfe84978c066934 to your computer and use it in GitHub Desktop.
Save xhackjp1/5ffd4641889d5eb28bfe84978c066934 to your computer and use it in GitHub Desktop.
200行のオセロコード
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>オセロゲーム -CANVAS-</h1>
<canvas width="400" height="400" id="canvas" style="background-color: green"></canvas>
<script>
// ここにオセロのコードを書いていきます
const WHITE = 1;
const BLACK = 2;
const FIELD_SIZE = 400;
const BLOCK_SIZE = FIELD_SIZE / 8;
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let currentPlayer = "white";
const directions = [
{ name: "上方向", x: 0, y: -1 },
{ name: "下方向", x: 0, y: 1 },
{ name: "左方向", x: -1, y: 0 },
{ name: "右方向", x: 1, y: 0 },
{ name: "左上方向", x: -1, y: -1 },
{ name: "左下方向", x: -1, y: 1 },
{ name: "右下方向", x: 1, y: 1 },
{ name: "右上方向", x: 1, y: -1 },
];
// ---------- 盤面データ
// banmenの二次元配列データを元に、canvasに石を実際に配置する
const banmen = [
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 2, 1, 0, 0, 0],
[0, 0, 0, 1, 2, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
];
// 8 x 8 => 64区画を描画する
banmen.drawReversiFiledLines = () => {
// 線の幅
ctx.lineWidth = 1;
for (let i = 1; i < 8; i++) {
ctx.beginPath();
ctx.moveTo(0, BLOCK_SIZE * i);
ctx.lineTo(FIELD_SIZE, BLOCK_SIZE * i);
ctx.moveTo(BLOCK_SIZE * i, 0);
ctx.lineTo(BLOCK_SIZE * i, FIELD_SIZE);
ctx.closePath();
ctx.stroke();
}
};
// banmenデータから石を描画
banmen.refresh = function () {
for (let x = 0; x < 8; x++) {
for (let y = 0; y < 8; y++) {
if (this[y][x] !== 0) {
drawStone(x, y, this[y][x]);
}
}
}
};
banmen.drawReversiFiledLines();
banmen.refresh();
// 盤面データ ----------
class Stone {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
}
}
// 石がその位置に置けるかを調べる関数
function canPutStone(originX, originY, color, simulation = false) {
const canReverse = false; // ひっくり返せるかのフラグ
const reverseStoneColor = color === WHITE ? BLACK : WHITE; // 手番ではない石の色
let allStones = []; // ひっくり返せる石を格納しておく配列
if(banmen[originY][originX] !== 0) {
// 空じゃないマスには置けない
return false
}
// 石を置きたい場所の八方向それぞれについて石がどのように配置されているか調べる
directions.forEach((direction) => {
let x = originX;
let y = originY;
const stones = [];
// 最大7回繰り返し、石の情報を集める
for (let i = 0; i < 7; i++) {
x += direction.x;
y += direction.y;
if (x > 7 || x < 0 || y > 7 || y < 0) {
break; // 盤面の外なので、ループ処理を抜ける
}
const current = banmen[y][x];
if (current === 0) {
break; // 何も置いてないマスがある場合、ループ処理を抜ける
}
// 石の情報を配列に追加
stones.push(new Stone(x, y, current));
if (current === color) {
break; // 先頭以外で、自分の色が出たらループ処理を抜ける
}
}
if (stones.length <= 1) {
return; // 一つしか配列がない場合はひっくり返せる石はない
}
if (!stones[0] && stones[0].color === reverseStoneColor) {
return; // 配列の先頭が相手の石の色でない場合、ひっくり返せない
}
// 末尾の石の色が自分と同じ色である場合、ひっくり返せる
let lastIndex = stones.length - 1;
if (stones[lastIndex].color === color) {
console.log(`手番: ${currentPlayer} x:${originX}, y:${originY} は ${direction.name} にひっくり返せる`);
// ひっくり返せる全ての石の配列を作る
// `...`はスプレッド構文
allStones = [...allStones, ...stones];
} else {
return;
}
});
// ひっくり返せる石の配列を見ての処理
if (simulation) {
// シミュレーション
if (allStones.length === 0) {
return false; // ひっくり返せる石がない
} else {
return true; // ひっくり返せる石が一つ以上ある
}
} else {
// 本番(実際に石を引っくり返す)
if (allStones.length === 0) {
return false; // ひっくり返せる石がない
}
drawStone(originX, originY, color); // 自分の石を置く場所
allStones.forEach((stone) => {
drawStone(stone.x, stone.y, color); // 自分の色にひっくりかえす
});
banmen.refresh();
// 手番の交代
currentPlayer = currentPlayer === "white" ? "black" : "white";
return true;
}
}
// クリックした時に石を置けるようになった
canvas.onclick = (e) => {
var rect = e.target.getBoundingClientRect();
mouseX = e.clientX - Math.floor(rect.left);
mouseY = e.clientY - Math.floor(rect.top);
const posX = Math.round((mouseX - 25) / 50);
const posY = Math.round((mouseY - 25) / 50);
// 石を置けるか判定
if (!canPutStone(posX, posY, currentPlayer === "white" ? 1 : 2, false)) {
alert("そこには置けません 手番: " + currentPlayer);
}
};
// 石を置く関数
function drawStone(x, y, color) {
if (color === 1) {
ctx.fillStyle = "white";
} else if (color === 2) {
ctx.fillStyle = "black";
}
banmen[y][x] = color;
ctx.beginPath();
ctx.arc(25 + x * 50, 25 + y * 50, 22, 0, 2 * Math.PI);
ctx.fill();
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>オセロゲーム -CANVAS-</h1>
<canvas width="400" height="400" id="canvas" style="background-color: green"></canvas>
<script>
// ここにオセロのコードを書いていきます
const WHITE = 1;
const BLACK = 2;
const FIELD_SIZE = 400;
const BLOCK_SIZE = FIELD_SIZE / 8;
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let currentPlayer = "white";
// 手番の交代
function changePlayer() {
currentPlayer = currentPlayer === "white" ? "black" : "white";
}
const directions = [
{ name: "上方向", x: 0, y: -1 },
{ name: "下方向", x: 0, y: 1 },
{ name: "左方向", x: -1, y: 0 },
{ name: "右方向", x: 1, y: 0 },
{ name: "左上方向", x: -1, y: -1 },
{ name: "左下方向", x: -1, y: 1 },
{ name: "右下方向", x: 1, y: 1 },
{ name: "右上方向", x: 1, y: -1 },
];
// ---------- 盤面データ
// banmenの二次元配列データを元に、canvasに石を実際に配置する
const banmen = [
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 2, 1, 0, 0, 0],
[0, 0, 0, 1, 2, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
];
// 8 x 8 => 64区画を描画する
banmen.drawReversiFiledLines = () => {
// 線の幅
ctx.lineWidth = 1;
for (let i = 1; i < 8; i++) {
ctx.beginPath();
ctx.moveTo(0, BLOCK_SIZE * i);
ctx.lineTo(FIELD_SIZE, BLOCK_SIZE * i);
ctx.moveTo(BLOCK_SIZE * i, 0);
ctx.lineTo(BLOCK_SIZE * i, FIELD_SIZE);
ctx.closePath();
ctx.stroke();
}
};
// banmenデータから石を描画
banmen.refresh = function () {
for (let x = 0; x < 8; x++) {
for (let y = 0; y < 8; y++) {
drawStone(x, y, this[y][x]);
}
}
};
banmen.drawReversiFiledLines();
banmen.refresh();
// 盤面データ ----------
class Stone {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
}
}
// 石がその位置に置けるかを調べる関数
function canPutStone(originX, originY, color, simulation = false) {
const canReverse = false; // ひっくり返せるかのフラグ
const reverseStoneColor = color === WHITE ? BLACK : WHITE; // 手番ではない石の色
let allStones = []; // ひっくり返せる石を格納しておく配列
if(banmen[originY][originX] !== 0) {
// 空じゃないマスには置けない
return false
}
// 石を置きたい場所の八方向それぞれについて石がどのように配置されているか調べる
directions.forEach((direction) => {
let x = originX;
let y = originY;
const stones = [];
// 最大7回繰り返し、石の情報を集める
for (let i = 0; i < 7; i++) {
x += direction.x;
y += direction.y;
// 盤面の外なので、ループ処理を抜ける
if (x > 7 || x < 0 || y > 7 || y < 0) break;
// 何も置いてないマスがある場合、ループ処理を抜ける
if (banmen[y][x] === 0) break;
// 石の情報を配列に追加
stones.push(new Stone(x, y, banmen[y][x]));
// 先頭以外で、自分の色が出たらその時点でループ処理を抜ける
if (banmen[y][x] === color) break;
}
// 一つしか配列がない場合はひっくり返せる石はない
if (stones.length <= 1) return;
// 配列の先頭が相手の石の色でない場合、ひっくり返せない
if (!stones[0] && stones[0].color === reverseStoneColor) return;
// 末尾の石の色が自分と同じ色である場合、ひっくり返せる
let lastIndex = stones.length - 1;
if (stones[lastIndex].color === color) {
console.log(`手番: ${currentPlayer} x:${originX}, y:${originY} は ${direction.name} にひっくり返せる`);
// ひっくり返せる全ての石の配列を作る `...` はスプレッド構文
allStones = [...allStones, ...stones];
} else {
return;
}
});
// ひっくり返せる石の配列を見ての処理
if (simulation) {
// シミュレーション
// この機能を使って、ひっくり返せる位置をユーザーに知らせる仕組みが作れる
if (allStones.length === 0) {
return false; // ひっくり返せる石がない
} else {
return true; // ひっくり返せる石が一つ以上ある
}
} else {
// 本番(実際に石を引っくり返す)
// ひっくり返せる石がない
if (allStones.length === 0) return false;
allStones.push(new Stone(originX, originY, color))
allStones.forEach((stone) => {
drawStone(stone.x, stone.y, color); // 自分の色にひっくりかえす
});
banmen.refresh();
// 手番の交代
changePlayer();
return true;
}
}
// クリックした時に石を置く関数
canvas.onclick = (e) => {
var rect = e.target.getBoundingClientRect();
mouseX = e.clientX - Math.floor(rect.left);
mouseY = e.clientY - Math.floor(rect.top);
const posX = Math.round((mouseX - 25) / 50);
const posY = Math.round((mouseY - 25) / 50);
// 石を置けるか判定
if (!canPutStone(posX, posY, currentPlayer === "white" ? 1 : 2, false)) {
alert("そこには置けません 手番: " + currentPlayer); // 置ける場合は置く
}
};
// 石を置く関数
function drawStone(x, y, color) {
if (color === 1) {
ctx.fillStyle = "white";
} else if (color === 2) {
ctx.fillStyle = "black";
} else {
return; // 何もしない
}
banmen[y][x] = color;
ctx.beginPath();
ctx.arc(25 + x * 50, 25 + y * 50, 22, 0, 2 * Math.PI);
ctx.fill();
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>オセロゲーム -CANVAS-</h1>
<canvas width="400" height="400" id="canvas" style="background-color: green"></canvas>
<script>
// ここにオセロのコードを書いていきます
const WHITE = 1;
const BLACK = 2;
const FIELD_SIZE = 400;
const BLOCK_SIZE = FIELD_SIZE / 8;
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let currentPlayer = "white";
// 手番の交代
function changePlayer() {
currentPlayer = currentPlayer === "white" ? "black" : "white";
}
const directions = [
{ name: "上方向", x: 0, y: -1 },
{ name: "下方向", x: 0, y: 1 },
{ name: "左方向", x: -1, y: 0 },
{ name: "右方向", x: 1, y: 0 },
{ name: "左上方向", x: -1, y: -1 },
{ name: "左下方向", x: -1, y: 1 },
{ name: "右下方向", x: 1, y: 1 },
{ name: "右上方向", x: 1, y: -1 },
];
// ---------- 盤面データ
// banmenの二次元配列データを元に、canvasに石を実際に配置する
const banmen = [
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 2, 1, 0, 0, 0],
[0, 0, 0, 1, 2, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
];
// 8 x 8 => 64区画を描画する
banmen.drawReversiFiledLines = () => {
// 線の幅
ctx.lineWidth = 1;
for (let i = 1; i < 8; i++) {
ctx.beginPath();
ctx.moveTo(0, BLOCK_SIZE * i);
ctx.lineTo(FIELD_SIZE, BLOCK_SIZE * i);
ctx.moveTo(BLOCK_SIZE * i, 0);
ctx.lineTo(BLOCK_SIZE * i, FIELD_SIZE);
ctx.closePath();
ctx.stroke();
}
};
// banmenデータから石を描画
banmen.refresh = function () {
for (let x = 0; x < 8; x++) {
for (let y = 0; y < 8; y++) {
drawStone(x, y, this[y][x]);
}
}
};
// 特定の方向に配置してあるひっくり返せる石の情報を集める
banmen.collectStones = function (x, y, color, direction) {
const stones = []
// 最大7回繰り返し、石の情報を集める
for (let i = 0; i < 7; i++) {
x += direction.x;
y += direction.y;
// 盤面の外なので、ループ処理を抜ける
if (x > 7 || x < 0 || y > 7 || y < 0) break;
// 何も置いてないマスがある場合、ループ処理を抜ける
if (this[y][x] === 0) break;
// 石の情報を配列に追加
stones.push(new Stone(x, y, this[y][x]));
// 先頭以外で、自分の色が出たらその時点でループ処理を抜ける
if (this[y][x] === color) break;
}
return stones;
}
banmen.drawReversiFiledLines();
banmen.refresh();
// 盤面データ ----------
class Stone {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
}
}
// 石がその位置に置けるかを調べる関数
function canPutStone(originX, originY, color, simulation = false) {
const canReverse = false; // ひっくり返せるかのフラグ
const reverseStoneColor = color === WHITE ? BLACK : WHITE; // 手番ではない石の色
let allStones = []; // ひっくり返せる石を格納しておく配列
if(banmen[originY][originX] !== 0) return false; // 空じゃないマスには置けない
// 石を置きたい場所の八方向それぞれについて石がどのように配置されているか調べる
directions.forEach((direction) => {
// 特定方向の石を配列に貯める
const stones = banmen.collectStones(originX, originY, color, direction);
// 一つ以下しか配列がない場合、ひっくり返せない
if (stones.length <= 1) return;
// 配列の先頭が相手の石の色でない場合、ひっくり返せない
if (!stones[0] && stones[0].color === reverseStoneColor) return;
// 末尾の石の色が自分と同じ色である場合、ひっくり返せる
let lastIndex = stones.length - 1;
if (stones[lastIndex].color === color) {
console.log(`手番: ${currentPlayer} x:${originX}, y:${originY} は ${direction.name} にひっくり返せる`);
// ひっくり返せる全ての石の配列を作る
allStones = [...allStones, ...stones]
}
});
// ひっくり返せる石の配列を見ての処理
if (simulation) {
// シミュレーション
// この機能を使って、ひっくり返せる位置をユーザーに知らせる仕組みが作れる
return allStones.length !== 0;
} else {
// 本番(実際に石を引っくり返す)
// ひっくり返せる石がない
if (allStones.length === 0) return false;
allStones.push(new Stone(originX, originY, color));
allStones.forEach(stone => drawStone(stone.x, stone.y, color)); // 自分の色にひっくりかえす
// 手番の交代
changePlayer();
banmen.refresh();
return true;
}
}
// クリックした時に石を置く関数
canvas.onclick = (e) => {
var rect = e.target.getBoundingClientRect();
mouseX = e.clientX - Math.floor(rect.left);
mouseY = e.clientY - Math.floor(rect.top);
const posX = Math.round((mouseX - BLOCK_SIZE/2) / BLOCK_SIZE);
const posY = Math.round((mouseY - BLOCK_SIZE/2) / BLOCK_SIZE);
// 石を置けるか判定
if (!canPutStone(posX, posY, currentPlayer === "white" ? 1 : 2, false)) {
alert("そこには置けません 手番: " + currentPlayer); // 置ける場合は置く
}
};
// 石を置く関数
function drawStone(x, y, color) {
if (color === 1) {
ctx.fillStyle = "white";
} else if (color === 2) {
ctx.fillStyle = "black";
} else {
return; // 何もしない
}
banmen[y][x] = color;
ctx.beginPath();
ctx.arc(25 + x * 50, 25 + y * 50, 22, 0, 2 * Math.PI);
ctx.fill();
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>オセロゲーム -CANVAS-</h1>
<canvas width="400" height="400" id="canvas" style="background-color: green"></canvas>
<script>
// ここにオセロのコードを書いていきます
const WHITE = 1;
const BLACK = 2;
const FIELD_SIZE = 400;
const BLOCK_SIZE = FIELD_SIZE / 8;
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let currentPlayer = "white";
// 手番の交代
function changePlayer() {
currentPlayer = currentPlayer === "white" ? "black" : "white";
}
const directions = [
{ name: "上方向", x: 0, y: -1 },
{ name: "下方向", x: 0, y: 1 },
{ name: "左方向", x: -1, y: 0 },
{ name: "右方向", x: 1, y: 0 },
{ name: "左上方向", x: -1, y: -1 },
{ name: "左下方向", x: -1, y: 1 },
{ name: "右下方向", x: 1, y: 1 },
{ name: "右上方向", x: 1, y: -1 },
];
// ---------- 盤面データ
// banmenの二次元配列データを元に、canvasに石を実際に配置する
const banmen = [
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 2, 1, 0, 0, 0],
[0, 0, 0, 1, 2, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
];
// 8 x 8 => 64区画を描画する
banmen.drawReversiFiledLines = () => {
// 線の幅
ctx.lineWidth = 1;
for (let i = 1; i < 8; i++) {
ctx.beginPath();
ctx.moveTo(0, BLOCK_SIZE * i);
ctx.lineTo(FIELD_SIZE, BLOCK_SIZE * i);
ctx.moveTo(BLOCK_SIZE * i, 0);
ctx.lineTo(BLOCK_SIZE * i, FIELD_SIZE);
ctx.closePath();
ctx.stroke();
}
};
// banmenデータから石を描画
banmen.refresh = function () {
for (let x = 0; x < 8; x++) {
for (let y = 0; y < 8; y++) {
drawStone(x, y, this[y][x]);
}
}
};
// 特定の方向に配置してあるひっくり返せる石の情報を集める
banmen.collectStones = function (x, y, color, direction) {
const reverseStoneColor = color === WHITE ? BLACK : WHITE; // 手番ではない石の色
const stones = []
// 最大7回繰り返し、石の情報を集める
for (let i = 0; i < 7; i++) {
x += direction.x;
y += direction.y;
// 盤面の外なので、ループ処理を抜ける
if (x > 7 || x < 0 || y > 7 || y < 0) break;
// 何も置いてないマスがある場合、ループ処理を抜ける
if (this[y][x] === 0) break;
// 石の情報を配列に追加
stones.push(new Stone(x, y, this[y][x]));
// 先頭以外で、自分の色が出たらその時点でループ処理を抜ける
if (this[y][x] === color) break;
}
// 一つ以下しか配列がない場合、ひっくり返せない
// ひっくり返せない状況の場合は、空の配列を返す
if (stones.length <= 1) return [];
// 配列の先頭が相手の石の色でない場合、ひっくり返せない
if (stones[0].color !== reverseStoneColor) return [];
// 末尾の石の色が自分と同じ色でない場合、ひっくり返せない
if (stones[stones.length - 1].color !== color) return [];
return stones;
}
banmen.drawReversiFiledLines();
banmen.refresh();
// 盤面データ ----------
class Stone {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
}
}
// 石がその位置に置けるかを調べる関数
function canPutStone(originX, originY, color, simulation = false) {
const canReverse = false; // ひっくり返せるかのフラグ
let allStones = []; // ひっくり返せる石を格納しておく配列
if(banmen[originY][originX] !== 0) return false; // 空じゃないマスには置けない
// 石を置きたい場所の八方向それぞれについて石がどのように配置されているか調べる
directions.forEach((direction) => {
// 特定方向の石を配列に貯める
const stones = banmen.collectStones(originX, originY, color, direction);
// ひっくり返せる全ての石の配列を作る
//allStones = [...allStones, ...stones]
allStones = allStones.concat(stones)
});
// ひっくり返せる石の配列を見ての処理
if (simulation) {
// シミュレーション
// この機能を使って、ひっくり返せる位置をユーザーに知らせる仕組みが作れる
return allStones.length !== 0;
} else {
// 本番(実際に石を引っくり返す)
// ひっくり返せる石がない
if (allStones.length === 0) return false;
allStones.push(new Stone(originX, originY, color));
allStones.forEach(stone => drawStone(stone.x, stone.y, color)); // 自分の色にひっくりかえす
// 手番の交代
changePlayer();
banmen.refresh();
return true;
}
}
// クリックした時に石を置く関数
canvas.onclick = (e) => {
var rect = e.target.getBoundingClientRect();
mouseX = e.clientX - Math.floor(rect.left);
mouseY = e.clientY - Math.floor(rect.top);
const posX = Math.round((mouseX - BLOCK_SIZE/2) / BLOCK_SIZE);
const posY = Math.round((mouseY - BLOCK_SIZE/2) / BLOCK_SIZE);
// 石を置けるか判定
if (!canPutStone(posX, posY, currentPlayer === "white" ? 1 : 2, false)) {
alert("そこには置けません 手番: " + currentPlayer); // 置ける場合は置く
}
};
// 石を置く関数
function drawStone(x, y, color) {
if (color === 1) {
ctx.fillStyle = "white";
} else if (color === 2) {
ctx.fillStyle = "black";
} else {
return; // 何もしない
}
banmen[y][x] = color;
ctx.beginPath();
ctx.arc(25 + x * 50, 25 + y * 50, 22, 0, 2 * Math.PI);
ctx.fill();
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>オセロゲーム -CANVAS-</h1>
<canvas width="400" height="400" id="canvas" style="background-color: green"></canvas>
<script>
// ここにオセロのコードを書いていきます
const WHITE = 1;
const BLACK = 2;
const COLUMN_SIZE = 8;
const FIELD_SIZE = 400;
const BLOCK_SIZE = FIELD_SIZE / COLUMN_SIZE;
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let currentPlayer = "white";
// 手番の交代
function changePlayer() {
currentPlayer = currentPlayer === "white" ? "black" : "white";
}
const directions = [
{ name: "上方向", x: 0, y: -1 },
{ name: "下方向", x: 0, y: 1 },
{ name: "左方向", x: -1, y: 0 },
{ name: "右方向", x: 1, y: 0 },
{ name: "左上方向", x: -1, y: -1 },
{ name: "左下方向", x: -1, y: 1 },
{ name: "右下方向", x: 1, y: 1 },
{ name: "右上方向", x: 1, y: -1 },
];
// ---------- 盤面データ
// banmenの二次元配列データを元に、canvasに石を実際に配置する
const banmen = [
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 2, 1, 0, 0, 0],
[0, 0, 0, 1, 2, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
];
// 8 x 8 => 64区画を描画する
banmen.drawReversiFiledLines = () => {
// 線の幅
ctx.fillStyle = "green"
ctx.fillRect(0, 0, FIELD_SIZE, FIELD_SIZE)
ctx.lineWidth = 1;
for (let i = 1; i < COLUMN_SIZE; i++) {
ctx.beginPath();
ctx.moveTo(0, BLOCK_SIZE * i);
ctx.lineTo(FIELD_SIZE, BLOCK_SIZE * i);
ctx.moveTo(BLOCK_SIZE * i, 0);
ctx.lineTo(BLOCK_SIZE * i, FIELD_SIZE);
ctx.closePath();
ctx.stroke();
}
};
// banmenデータから石を描画
banmen.refresh = function () {
banmen.drawReversiFiledLines()
for (let x = 0; x < COLUMN_SIZE; x++) {
for (let y = 0; y < COLUMN_SIZE; y++) {
drawStone(x, y, this[y][x]);
}
}
};
// banmenデータから石が置けるマスを目立たせる
banmen.highlight = function () {
const MARGIN_SIZE = 2;
let playerColor = currentPlayer === "white" ? 1 : 2;
for (let x = 0; x < COLUMN_SIZE; x++) {
for (let y = 0; y < COLUMN_SIZE; y++) {
if(canPutStone(x, y, playerColor, true)){
ctx.fillStyle = "#3cb371";
ctx.fillRect(x * BLOCK_SIZE + MARGIN_SIZE, y * BLOCK_SIZE + MARGIN_SIZE, 46, 46);
}
}
}
};
// 特定の方向に配置してあるひっくり返せる石の情報を集める
banmen.collectStones = function (x, y, color, direction) {
const stoneColorToReverse = color === WHITE ? BLACK : WHITE; // 手番ではない石の色
const FIRST_COLUMN_INDEX = 0
const LAST_COLUMN_INDEX = 7
const stones = [];
// 最大7回繰り返し、石の情報を集める
for (let i = FIRST_COLUMN_INDEX; i < LAST_COLUMN_INDEX; i++) {
x += direction.x;
y += direction.y;
// 盤面の外なので、ループ処理を抜ける
if (x > LAST_COLUMN_INDEX || x < FIRST_COLUMN_INDEX || y > LAST_COLUMN_INDEX || y < FIRST_COLUMN_INDEX) break;
// 何も置いてないマスがある場合、ループ処理を抜ける
if (this[y][x] === 0) break;
// 石の情報を配列に追加
stones.push(new Stone(x, y, this[y][x]));
// 先頭以外で、自分の色が出たらその時点でループ処理を抜ける
if (this[y][x] === color) break;
}
// 一つ以下しか配列がない場合、ひっくり返せない
// ひっくり返せない状況の場合は、空の配列を返す
if (stones.length <= 1) return [];
// 配列の先頭が相手の石の色でない場合、ひっくり返せない
if (stones[0].color !== stoneColorToReverse) return [];
// 末尾の石の色が自分と同じ色でない場合、ひっくり返せない
if (stones[stones.length - 1].color !== color) return [];
return stones;
}
banmen.drawReversiFiledLines();
banmen.refresh();
// 盤面データ ----------
class Stone {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
}
}
// 石がその位置に置けるかを調べる関数
function canPutStone(originX, originY, color, simulation = false) {
let allStones = []; // ひっくり返せる石を格納しておく配列
if(banmen[originY][originX] !== 0) return false; // 空じゃないマスには置けない
// 石を置きたい場所の八方向それぞれについて石がどのように配置されているか調べる
directions.forEach((direction) => {
// 特定方向で、ひっくり返せる石の情報を配列に貯める
const stones = banmen.collectStones(originX, originY, color, direction);
// ひっくり返せる全ての石の配列を作る
//allStones = [...allStones, ...stones]
allStones = allStones.concat(stones)
});
// ひっくり返せる石があるか?
const canReverse = allStones.length > 0
// シミュレーションの場合は、ひっくり返せるかだけ返す
// この機能を使って、ひっくり返せる位置をユーザーに知らせる仕組みが作れる
if (simulation) return canReverse;
// 本番(実際に石を引っくり返す)
if (canReverse) {
// ひっくり返せる場合の処理
allStones.push(new Stone(originX, originY, color));
allStones.forEach(stone => drawStone(stone.x, stone.y, color)); // 自分の色にひっくりかえす
// 手番の交代
changePlayer();
banmen.refresh();
banmen.highlight();
}
return canReverse;
}
// クリックした時に石を置く関数
canvas.onclick = (e) => {
var rect = e.target.getBoundingClientRect();
mouseX = e.clientX - Math.floor(rect.left);
mouseY = e.clientY - Math.floor(rect.top);
const posX = Math.round((mouseX - BLOCK_SIZE/2) / BLOCK_SIZE);
const posY = Math.round((mouseY - BLOCK_SIZE/2) / BLOCK_SIZE);
// 石を置けるか判定
if (!canPutStone(posX, posY, currentPlayer === "white" ? 1 : 2, false)) {
alert("そこには置けません 手番: " + currentPlayer); // 置ける場合は置く
}
};
// 石を置く関数
function drawStone(x, y, color) {
if (color === 1) {
ctx.fillStyle = "white";
} else if (color === 2) {
ctx.fillStyle = "black";
} else {
return; // 何もしない
}
banmen[y][x] = color;
ctx.beginPath();
ctx.arc(25 + x * 50, 25 + y * 50, 22, 0, 2 * Math.PI);
ctx.fill();
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment