Created
May 1, 2020 13:50
-
-
Save tamamu/8e2803ede64dedc0ba608ccb09f0a1df to your computer and use it in GitHub Desktop.
N-moku written in JavaScript (p5.js)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Player = { | |
NONE: 0, | |
RED: 1, | |
BLUE: 2, | |
} | |
let global = { | |
size: {x: 5, y: 5}, | |
left: 640/2 - 300/2, | |
top: 10, | |
width: 300, | |
height: 300, | |
logs: [], | |
game: null, | |
turn: Player.RED, | |
requireCount: 3, | |
isGameOver: false, | |
autoplay: true, | |
delta: 0, | |
}; | |
console.log = msg => global.logs.push(msg); | |
function setup() { | |
const {size} = global; | |
reset(); | |
updateStage(); | |
} | |
function reset() { | |
const {size} = global; | |
global.logs = [] | |
global.turn = 1; | |
global.isGameOver = false; | |
global.game = new Array(size.y) | |
.fill() | |
.map(() => ( | |
new Array(size.x) | |
.fill() | |
.map(() => Player.NONE))) | |
notice(); | |
} | |
function updateStage() { | |
const {size, left, top, width, height, game} = global; | |
createCanvas(640, 480); | |
fill("red"); | |
rect(0, 0, 320, 480); | |
fill("blue") | |
rect(320, 0, 320, 480) | |
fill("white"); | |
stroke("black"); | |
rect(left, top, width, height); | |
for (let i=1; i < size.y; i++) { | |
const y = height/size.y*i; | |
line(left, top+y, left+width, top+y); | |
} | |
for (let i=1; i < size.x; i++) { | |
const x = width/size.x*i; | |
line(left+x, top, left+x, top+height); | |
} | |
for (let x=0; x < size.x; x++) { | |
for (let y=0; y < size.y; y++) { | |
switch (game[y][x]) { | |
case Player.NONE: | |
fill(0, 0, 0, 0); | |
break; | |
case Player.RED: | |
fill("red"); | |
break; | |
case Player.BLUE: | |
fill("blue"); | |
break; | |
} | |
stroke(0, 0, 0, 0); | |
ellipse( | |
left + x * width / size.x + width / (size.x*2), | |
top + y * height / size.y + height / (size.y*2), | |
width / size.x * 0.75, | |
height / size.y * 0.75, | |
); | |
} | |
} | |
} | |
function checkGameOver() { | |
const {size, game, turn, requireCount} = global; | |
let win = false; | |
for (let y=0; y < size.y; y++) { | |
for (let x=0; x < size.x; x++) { | |
if (game[y][x] !== turn) continue; | |
// right | |
let rightAccum = 0; | |
for (let i=0; x+i < size.x; i++) { | |
if (game[y][x+i] === turn) { | |
rightAccum += 1; | |
} else { | |
break; | |
} | |
} | |
if (rightAccum >= requireCount) { | |
win = true; | |
break; | |
} | |
// down-right | |
drAccum = 0; | |
if (size.x - x < size.y - y) { // down-axis is long | |
for (let i=0; | |
x + i < size.x; | |
i++) | |
{ | |
if (game[y+i][x+i] === turn) { | |
drAccum += 1; | |
} else { | |
break; | |
} | |
} | |
} else { | |
for (let i=0; | |
y + i < size.y; | |
i++) | |
{ | |
if (game[y+i][x+i] === turn) { | |
drAccum += 1; | |
} else { | |
break; | |
} | |
} | |
} | |
if (drAccum >= requireCount) { | |
win = true; | |
break; | |
} | |
// down-left | |
dlAccum = 0; | |
if (x < size.y - y) { // down-axis is long | |
for (let i=0; | |
x - i >= 0; | |
i++) | |
{ | |
if (game[y+i][x-i] === turn) { | |
dlAccum += 1; | |
} else { | |
break; | |
} | |
} | |
} else { | |
for (let i=0; | |
y + i < size.y; | |
i++) | |
{ | |
if (game[y+i][x-i] === turn) { | |
dlAccum += 1; | |
} else { | |
break; | |
} | |
} | |
} | |
if (dlAccum >= requireCount) { | |
win = true; | |
break; | |
} | |
// down | |
let downAccum = 0; | |
for (let i=0; y+i < game.length; i++) { | |
if (game[y+i][x] === turn) { | |
downAccum += 1; | |
} else { | |
break; | |
} | |
} | |
if (downAccum >= requireCount) { | |
win = true; | |
break; | |
} | |
} | |
if (win) { | |
break; | |
} | |
} | |
if (win) { | |
console.log(`${turn === Player.RED ? "赤" : "青"}の勝ち!なんで負けたか明日まで考えといてください。`); | |
console.log(`クリックでNEW GAME!!`); | |
global.isGameOver = true; | |
return true; | |
} | |
// check draw | |
let fillCount = false; | |
for (let y=0; y < size.y; y++) { | |
for (let x=0; x < size.x; x++) { | |
if (game[y][x] !== Player.NONE) { | |
fillCount += 1; | |
} | |
} | |
} | |
if (fillCount === size.x * size.y) { | |
console.log("引き分け!"); | |
console.log("クリックでNEW GAME!!"); | |
global.isGameOver = true; | |
return true; | |
} | |
} | |
function draw() { | |
global.delta += deltaTime; | |
const {left, top, width, height, logs, autoplay, isGameOver} = global; | |
fill("white"); | |
stroke("black"); | |
rect(0, top+height, 640, 480-(top+height)); | |
fill("black"); | |
stroke(0, 0, 0, 0); | |
textSize(16); | |
for (let i=0; i < Math.min(logs.length, 10); i++) { | |
text(logs[i+Math.max(logs.length-10, 0)], 0, top+height+i*16+16); | |
} | |
if (global.delta > 10 && autoplay) { | |
if (isGameOver) { | |
reset(); | |
updateStage(); | |
} else { | |
auto(); | |
global.delta = 0; | |
} | |
} | |
} | |
function auto() { | |
const {size, game} = global; | |
let nones = [] | |
for (let y=0; y < size.y; y++) { | |
for (let x=0; x < size.x; x++) { | |
if (game[y][x] === Player.NONE) { | |
nones.push([x, y]); | |
} | |
} | |
} | |
if (nones.length > 0) { | |
const next = nones[Math.floor(Math.random() * nones.length)]; | |
put(next[0], next[1]) | |
} | |
} | |
function mouseClicked(ev) { | |
const {size, left, top, width, height, isGameOver} = global; | |
if (isGameOver) { | |
reset(); | |
updateStage(); | |
return; | |
} | |
const x = Math.floor((ev.clientX - left) / (width / size.x)); | |
const y = Math.floor((ev.clientY - top) / (height / size.y)); | |
put(x, y); | |
} | |
function put(x, y) { | |
const {size, game, turn} = global; | |
if (x < 0 || y < 0 || size.x <= x || size.y <= y) return; | |
if (game[y][x] !== 0) return; | |
game[y][x] = turn; | |
updateStage(); | |
if (checkGameOver()) return; | |
global.turn += 1; | |
if (global.turn > 2) global.turn = Player.RED; | |
notice(); | |
} | |
function notice() { | |
console.log(`${global.turn === Player.RED ? "赤" : "青"}の番やで`); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment