スマホでプログラミング専用ツール「ミニコデ」には大きな欠点があります。
それは小さな開発しかできないことです。
そして、軽量化、高速化を図るために機能も制限しています。

ミニコデでは満足な開発ができない!
という方もいらっしゃると思いますので、ここからは本気で開発のための環境構築について説明していきます。
まず、その前に皆さんに少しだけレベルアップしてもらいます。
JavaScriptはメモ帳で開発できる!?
Windowsをお使いの方であれば、一度は触ったことがあるであろうメモ帳。
実はメモ帳で開発できてしまうのです。ということで、手順を解説いたします。
ご覧の通り、あのメモ帳です。サンプルのソースコードも用意してありますので、そちらをお使いください。

サンプルのソースコードはこちら
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>リバーシ(人 vs CPU)</title>
<style>
:root{
--bg:#0b1020;
--board:#0d5c34;
--board-dark:#0a4b2a;
--cell:#0f6a3c;
--cell-alt:#0c5b33;
--accent:#66e0a3;
--text:#f6f7fb;
--muted:#aab3c2;
--danger:#ff6b6b;
--shadow: 0 10px 25px rgba(0,0,0,.25);
--radius: 18px;
}
html,body{margin:0;background:radial-gradient(1200px 600px at 70% -10%, #16213e 0%, #0e152b 45%, var(--bg) 100%);color:var(--text);font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Hiragino Kaku Gothic ProN", "Noto Sans JP", "Yu Gothic", "Helvetica Neue", Arial, "Apple Color Emoji","Segoe UI Emoji";}
.wrap{max-width:920px;margin:clamp(8px,2vw,24px) auto;padding:clamp(8px,2vw,20px);}
header{display:flex;gap:12px;align-items:center;justify-content:space-between;margin-bottom:12px;flex-wrap:wrap}
h1{font-size:clamp(20px,3.8vw,28px);margin:0;letter-spacing:.02em}
.sub{font-size:12px;color:var(--muted)}
.panel{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.badge{background:rgba(255,255,255,.06);padding:8px 10px;border-radius:999px;display:flex;gap:10px;align-items:center;box-shadow:var(--shadow)}
.dot{width:14px;height:14px;border-radius:50%}
.dot.black{background:#0b0b0b;box-shadow: inset 0 2px 0 rgba(255,255,255,.15), 0 0 0 2px rgba(255,255,255,.06)}
.dot.white{background:#fafafa;box-shadow: inset 0 2px 0 rgba(255,255,255,.8), 0 0 0 2px rgba(0,0,0,.06)}
.score{display:flex;gap:10px;align-items:center}
.score span{font-variant-numeric: tabular-nums}
.controls{display:flex;gap:8px;flex-wrap:wrap}
button, select{
background: linear-gradient(180deg, rgba(255,255,255,.12), rgba(255,255,255,.04));
color:var(--text);border:1px solid rgba(255,255,255,.12);
padding:10px 14px;border-radius:12px;cursor:pointer;
font-weight:600;letter-spacing:.02em;
box-shadow:var(--shadow);backdrop-filter: blur(6px);
}
button:disabled{opacity:.5;cursor:not-allowed}
button.ghost{background:transparent;border-color:rgba(255,255,255,.18)}
button.danger{border-color:rgba(255,0,0,.25)}
.grid{
width:min(92vw, 720px);
aspect-ratio:1/1;
margin:12px auto;
background:radial-gradient(600px 300px at 10% -10%, #147a45, var(--board)) no-repeat;
border-radius:var(--radius);
padding:10px;
box-shadow: var(--shadow), inset 0 0 0 1px rgba(255,255,255,.06);
display:grid;
grid-template-columns: repeat(8, 1fr);
grid-template-rows: repeat(8, 1fr);
gap:6px;
touch-action: manipulation;
}
.cell{
background:linear-gradient(180deg, var(--cell), var(--board-dark));
border-radius:10px;
position:relative;
box-shadow: inset 0 2px 0 rgba(255,255,255,.08), inset 0 -2px 0 rgba(0,0,0,.2);
user-select:none;
}
.cell:nth-child(odd){ background: linear-gradient(180deg, var(--cell-alt), var(--board-dark)); }
.cell.valid::after{
content:"";
position:absolute;inset:8px;border-radius:50%;
border:2px dashed rgba(255,255,255,.35);
opacity:.75;pointer-events:none;
animation: pulse 1.2s infinite ease-in-out;
}
.cell.hint::after{
border:0;background:radial-gradient(circle at 50% 55%, rgba(255,255,255,.25), rgba(255,255,255,.05) 60%, transparent 61%);
}
@keyframes pulse{0%,100%{opacity:.35}50%{opacity:.8}}
.disc{
position:absolute;inset:10%;
border-radius:50%;
box-shadow: inset 0 8px 0 rgba(255,255,255,.18), 0 6px 12px rgba(0,0,0,.5);
transform: scale(.9);
transition: transform .18s ease, box-shadow .2s ease, background .2s ease, filter .2s ease;
will-change: transform, filter;
}
.disc.black{ background: #0b0b0b; }
.disc.white{ background: #fafafa; box-shadow: inset 0 8px 0 rgba(255,255,255,.8), 0 6px 12px rgba(0,0,0,.35)}
.flip{animation: flip .2s ease}
@keyframes flip{
0%{ transform: rotateY(0) scale(.9) }
50%{ transform: rotateY(90deg) scale(.9) }
100%{ transform: rotateY(180deg) scale(.9) }
}
.status{
margin:8px auto 0;
text-align:center;
color:var(--muted);
min-height:1.4em;
}
.row-labels, .col-labels{display:grid;grid-template-columns:repeat(8,1fr);gap:6px;width:min(92vw, 720px);margin:0 auto;color:var(--muted);font-size:12px;letter-spacing:.08em}
.row-labels{grid-template-columns:repeat(8,1fr)}
.footer{margin-top:10px;text-align:center;color:var(--muted);font-size:12px}
.toggle{
appearance:none;width:44px;height:28px;border-radius:999px;display:inline-grid;place-items:center;position:relative;
background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.12);cursor:pointer;vertical-align:middle
}
.toggle::after{content:"";width:20px;height:20px;border-radius:50%;background:white;position:absolute;left:4px;transition:all .15s ease}
.toggle:checked::after{left:20px}
.kbd{font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono"; background:rgba(255,255,255,.06); border:1px solid rgba(255,255,255,.12); padding:2px 6px; border-radius:6px}
</style>
</head>
<body>
<div class="wrap">
<header>
<div>
<h1>リバーシ <span class="sub">(人 vs CPU / スマホ対応)</span></h1>
<div class="sub">先手は<strong>黒(あなた)</strong>です。置ける場所は点線で表示されます。</div>
</div>
<div class="panel">
<div class="badge score" id="scoreBox">
<span class="dot black"></span><span id="blackScore">2</span>
<span class="dot white" style="margin-left:8px"></span><span id="whiteScore">2</span>
</div>
<div class="badge" id="turnBox"><span class="dot black"></span><span id="turnText">あなたの手番</span></div>
</div>
</header>
<div class="controls">
<button id="newBtn">新規ゲーム</button>
<button id="undoBtn" class="ghost" title="一手戻す(取り消し)">UNDO</button>
<button id="hintBtn" class="ghost" title="候補に薄い石を表示">ヒント</button>
<label class="badge" style="gap:8px">
難易度
<select id="level">
<option value="1">かんたん</option>
<option value="2" selected>ふつう</option>
<option value="3">むずかしい</option>
</select>
</label>
<label class="badge" style="gap:8px" title="CPUの思考アニメを省略">
アニメ軽量
<input id="liteAnim" type="checkbox" class="toggle">
</label>
</div>
<div class="grid" id="board" aria-label="Reversi board" role="grid"></div>
<div class="status" id="status"></div>
<div class="footer">操作: マスをタップ / PCなら <span class="kbd">クリック</span>。ヒント: <span class="kbd">ヒント</span> ボタン。</div>
</div>
<script>
(function(){
const SIZE = 8;
const EMPTY = 0, BLACK = 1, WHITE = -1;
const DIRS = [[1,0],[-1,0],[0,1],[0,-1],[1,1],[1,-1],[-1,1],[-1,-1]];
const CORNER_WEIGHTS = [
[120,-20, 20, 5, 5, 20,-20,120],
[-20,-40, -5, -5, -5, -5,-40,-20],
[ 20, -5, 15, 3, 3, 15, -5, 20],
[ 5, -5, 3, 3, 3, 3, -5, 5],
[ 5, -5, 3, 3, 3, 3, -5, 5],
[ 20, -5, 15, 3, 3, 15, -5, 20],
[-20,-40, -5, -5, -5, -5,-40,-20],
[120,-20, 20, 5, 5, 20,-20,120],
];
const boardEl = document.getElementById('board');
const statusEl = document.getElementById('status');
const turnText = document.getElementById('turnText');
const blackScoreEl = document.getElementById('blackScore');
const whiteScoreEl = document.getElementById('whiteScore');
const newBtn = document.getElementById('newBtn');
const undoBtn = document.getElementById('undoBtn');
const hintBtn = document.getElementById('hintBtn');
const levelSel = document.getElementById('level');
const liteAnim = document.getElementById('liteAnim');
let state = {
board: makeInitialBoard(),
turn: BLACK, // human
human: BLACK,
cpu: WHITE,
history: [],
showHint:false,
lock:false,
};
buildGrid();
render(true);
attachHandlers();
function makeInitialBoard(){
const b = Array.from({length:SIZE}, ()=> Array(SIZE).fill(EMPTY));
const mid = SIZE/2;
b[mid-1][mid-1] = WHITE;
b[mid][mid] = WHITE;
b[mid-1][mid] = BLACK;
b[mid][mid-1] = BLACK;
return b;
}
function cloneBoard(b){ return b.map(row=>row.slice()); }
function inside(y,x){ return y>=0 && y<SIZE && x>=0 && x<SIZE; }
function validMoves(b, player){
const moves = [];
for(let y=0;y<SIZE;y++){
for(let x=0;x<SIZE;x++){
if(b[y][x]!==EMPTY) continue;
const flips = collectFlips(b,y,x,player);
if(flips.length) moves.push({y,x,flips});
}
}
return moves;
}
function collectFlips(b,y,x,player){
if(b[y][x]!==EMPTY) return [];
let flips = [];
for(const [dy,dx] of DIRS){
let ny=y+dy, nx=x+dx, tmp=[];
while(inside(ny,nx) && b[ny][nx]===-player){
tmp.push([ny,nx]); ny+=dy; nx+=dx;
}
if(tmp.length && inside(ny,nx) && b[ny][nx]===player) flips = flips.concat(tmp);
}
return flips;
}
function applyMove(b, move, player){
const nb = cloneBoard(b);
nb[move.y][move.x] = player;
for(const [fy,fx] of move.flips) nb[fy][fx] = player;
return nb;
}
function score(b){
let black=0, white=0;
for(let y=0;y<SIZE;y++) for(let x=0;x<SIZE;x++){
if(b[y][x]===BLACK) black++;
else if(b[y][x]===WHITE) white++;
}
return {black, white};
}
function gameOver(b){
return validMoves(b,BLACK).length===0 && validMoves(b,WHITE).length===0;
}
function buildGrid(){
boardEl.innerHTML = "";
for(let i=0;i<SIZE*SIZE;i++){
const cell = document.createElement('div');
cell.className = 'cell';
cell.setAttribute('role','gridcell');
cell.dataset.i = i;
cell.addEventListener('click', onCell);
boardEl.appendChild(cell);
}
}
function onCell(e){
if(state.lock) return;
const i = +e.currentTarget.dataset.i;
const y = Math.floor(i/ SIZE), x = i % SIZE;
const moves = validMoves(state.board, state.turn);
const mv = moves.find(m=>m.y===y && m.x===x);
if(!mv) return;
pushHistory();
state.board = applyMove(state.board, mv, state.turn);
animatePlace(y,x,state.turn);
state.turn = -state.turn;
state.showHint = false;
render();
stepIfCpuTurn();
}
function pushHistory(){
state.history.push({board: cloneBoard(state.board), turn: state.turn});
if(state.history.length>120) state.history.shift();
}
function popHistory(){
const last = state.history.pop();
if(last){
state.board = last.board;
state.turn = last.turn;
}
}
function render(initial=false){
const cells = boardEl.children;
const moves = validMoves(state.board, state.turn);
const moveSet = new Set(moves.map(m=>m.y*SIZE+m.x));
for(let y=0;y<SIZE;y++){
for(let x=0;x<SIZE;x++){
const idx = y*SIZE+x;
const el = cells[idx];
el.classList.toggle('valid', moveSet.has(idx) && !state.lock);
el.classList.toggle('hint', state.showHint && moveSet.has(idx));
// draw discs
let disc = el.querySelector('.disc');
const v = state.board[y][x];
if(v!==EMPTY){
if(!disc){
disc = document.createElement('div');
disc.className = 'disc ' + (v===BLACK?'black':'white') + (initial?'':' flip');
el.appendChild(disc);
}else{
const want = v===BLACK?'black':'white';
if(!disc.classList.contains(want)){
disc.classList.toggle('black'); disc.classList.toggle('white');
disc.classList.add('flip');
setTimeout(()=>disc && disc.classList.remove('flip'), 210);
}
}
}else{
if(disc) disc.remove();
}
}
}
const sc = score(state.board);
blackScoreEl.textContent = sc.black;
whiteScoreEl.textContent = sc.white;
const yourTurn = state.turn===state.human;
turnText.innerHTML = yourTurn ? "あなたの手番" : "CPU思考中…";
if(gameOver(state.board)){
const msg = sc.black===sc.white ? "引き分け!" : (sc.black>sc.white ? "あなたの勝ち!🎉" : "CPUの勝ち…");
statusEl.textContent = `ゲーム終了:${msg}`;
turnText.textContent = "終了";
}else{
const canMove = validMoves(state.board, state.turn).length>0;
statusEl.textContent = canMove? "" : "パスです。";
}
undoBtn.disabled = state.history.length===0;
}
function animatePlace(y,x,player){
if(liteAnim.checked) return;
const idx = y*SIZE+x;
const el = boardEl.children[idx];
const disc = el.querySelector('.disc');
if(disc){
disc.style.transform = 'scale(1.06)';
disc.style.filter = 'drop-shadow(0 0 8px rgba(102,224,163,.6))';
setTimeout(()=>{
if(!disc) return;
disc.style.transform = 'scale(.9)';
disc.style.filter = '';
}, 120);
}
}
function stepIfCpuTurn(){
if(gameOver(state.board)) { render(); return; }
const movesMe = validMoves(state.board, state.turn);
if(movesMe.length===0){
// pass
state.turn = -state.turn;
render();
if(state.turn===state.cpu) setTimeout(stepIfCpuTurn, 50);
return;
}
if(state.turn===state.cpu){
state.lock = true;
render();
const depth = levelSel.value==='1' ? 1 : (levelSel.value==='2' ? 2 : 3);
setTimeout(()=>{
const mv = aiBestMove(state.board, state.cpu, depth);
state.lock = false;
if(!mv){ // no move (shouldn't happen here)
state.turn = -state.turn; render(); return;
}
pushHistory();
state.board = applyMove(state.board, mv, state.cpu);
animatePlace(mv.y, mv.x, state.cpu);
state.turn = -state.cpu;
render();
// If human must pass, chain CPU
const nextMoves = validMoves(state.board, state.turn);
if(nextMoves.length===0 && !gameOver(state.board)){
state.turn = -state.turn;
render();
setTimeout(stepIfCpuTurn, 70);
}
}, liteAnim.checked ? 30 : 260);
}
}
// ---------- AI ----------
function aiBestMove(b, player, depth){
const moves = validMoves(b, player);
if(moves.length===0) return null;
// Opening heuristic: prefer corners
const corner = moves.find(m=>isCorner(m.y,m.x));
if(depth<=1 && corner) return corner;
let best = -Infinity, bestMove = moves[0];
let alpha = -Infinity, beta = Infinity;
for(const m of moves){
const nb = applyMove(b, m, player);
const val = -negamax(nb, -player, depth-1, -beta, -alpha);
if(val > best){ best = val; bestMove = m; }
if(val > alpha) alpha = val;
if(alpha >= beta) break; // alpha-beta pruning
}
return bestMove;
}
function negamax(b, player, depth, alpha, beta){
if(depth===0 || gameOver(b)) return evaluate(b, player);
const moves = validMoves(b, player);
if(moves.length===0){
if(validMoves(b, -player).length===0) return evaluate(b, player);
return -negamax(b, -player, depth-1, -beta, -alpha); // pass
}
let best = -Infinity;
for(const m of moves){
const nb = applyMove(b, m, player);
const val = -negamax(nb, -player, depth-1, -beta, -alpha);
if(val>best) best = val;
if(val>alpha) alpha = val;
if(alpha>=beta) break;
}
return best;
}
function evaluate(b, forPlayer){
// Heuristic: (corner-weighted material) + mobility + corner control + stability-ish
let mat = 0, myMoves=0, oppMoves=0, cornerCtrl=0, edges=0;
const s = score(b);
mat = (s.black - s.white) * (forPlayer===BLACK?1:-1);
for(let y=0;y<SIZE;y++){
for(let x=0;x<SIZE;x++){
const v = b[y][x];
if(v!==EMPTY){
const w = CORNER_WEIGHTS[y][x];
mat += (v===forPlayer? w : -w)*0.35;
if(isEdge(y,x)) edges += (v===forPlayer?1:-1)*0.4;
if(isCorner(y,x)) cornerCtrl += (v===forPlayer?1:-1)*20;
}
}
}
myMoves = validMoves(b, forPlayer).length;
oppMoves = validMoves(b, -forPlayer).length;
const mobility = (myMoves - oppMoves) * 2.2;
// Phase weighting: early emphasize position, late emphasize disks
const filled = s.black + s.white;
const phase = filled/64; // 0..1
const val = (1-phase)* (mobility + edges + cornerCtrl) + phase * (mat*0.8) + mat*0.15;
return val;
}
function isCorner(y,x){ return (y===0||y===SIZE-1) && (x===0||x===SIZE-1); }
function isEdge(y,x){ return y===0 || y===SIZE-1 || x===0 || x===SIZE-1; }
// ---------- UI Handlers ----------
function attachHandlers(){
newBtn.addEventListener('click', ()=>{
state.board = makeInitialBoard();
state.turn = BLACK;
state.history = [];
state.showHint = false;
render(true);
});
undoBtn.addEventListener('click', ()=>{
if(state.lock) return;
// Undo pair (CPU + Human) if CPU just moved; else undo one
popHistory(); // undo last
if(state.turn===state.cpu) popHistory(); // keep it user's turn
render();
});
hintBtn.addEventListener('click', ()=>{
state.showHint = !state.showHint;
render();
});
levelSel.addEventListener('change', ()=>{
// nothing special; affects next CPU move
});
// Keyboard support (optional)
window.addEventListener('keydown', (e)=>{
if(e.key==='h') { state.showHint=!state.showHint; render(); }
if((e.ctrlKey||e.metaKey) && e.key.toLowerCase()==='z'){ undoBtn.click(); }
if(e.key==='n') newBtn.click();
}, {passive:true});
}
})();
</script>
</body>
</html>名前をつけて保存で「すべてのファイル」を選択して、「index.html」というファイル名で保存してください。



ブラウザにオセロが表示されましたよね。
JavaScriptを動かすだけなら、特殊な環境は何一ついらないんです。
これを踏まえた上でもっと便利なツールをご紹介いたします。
JavaScriptの開発に便利な2つのツール
さて、本気で開発をするならメモ帳ではあっという間に限界を迎えてしまいます。
ということで、当方が実際に使っているツールをご紹介していきます。
Visual Studio Code(無料)

パソコンで本格的に開発するなら、まずおすすめしたいのが Visual Studio Code(ビジュアル・スタジオ・コード)、通称「VS Code」です。
マイクロソフトが無料で提供している開発環境で、世界中のエンジニアが利用しています。
因みに当方も基本はVSコードを使ってプログラムを書いています。電車の中はミニコデ、家に帰ったらVSコードみたいな使い方をしています。
VS Codeの魅力
- 無料で使えるのに高機能
HTML・CSS・JavaScriptはもちろん、PythonやC言語など幅広い言語に対応しています。 - 軽くてサクサク動く
高スペックPCでなくても快適に動作し、拡張機能を入れることで自分好みにカスタマイズできます。 - リアルタイムプレビューや補完機能
タグを打つと自動で閉じタグを入れてくれたり、色のプレビューを表示したりと、初心者にも優しい設計です。
ミニコデで慣れた「HTML・CSS・JavaScript」の構造をそのまま活かせるため、学習の延長線としても最適です。
VS Codeのセットアップ方法
以下のサイトよりダウンロードしてください。なお、Windows、Mac、Linux いずれのOSでもインストール可能です。

画面の案内に従ってセットアップします。特別な設定は不要で、初心者でも数分で完了します

ご自身のお使いになっているパソコンのOSにあったインストーラーをダウンロードしてください。

左側の「四角いアイコン」から以下の拡張機能を入れておくと快適です。

- Live Server:保存するとブラウザが自動で更新される
- Japanese Language Pack:メニューを日本語化できる
- Prettier:コードを自動で整形してくれる
これらは普通に入れておいた方が開発が楽になるのでオススメです。
スマホで使っていた感覚を活かそう
VS CodeはPC向けのツールですが、ミニコデでの操作感やコードの流れはそのまま通用します。HTML・CSS・JSの3ファイル構成も同じなので、移行のハードルは意外と低いはずです。
「スマホでできたから、パソコンでもできる」──その自信を持って、ぜひ挑戦してみてください。
CodePen(無料・有料プランあり)
「パソコンを持っていない」「インストールは面倒…」という方におすすめなのが、CodePen(コードペン) です。
これはブラウザ上で動くオンラインのプログラミング環境で、アカウントを作るだけですぐ使える のが魅力です。
CodePenの特徴
- ブラウザだけで動作する
ChromeやSafariなど、普段使っているブラウザ上でコードを書いてそのまま実行できます。 - HTML・CSS・JavaScriptを同時に編集できる
ミニコデと似た3ペイン構成で、変更がリアルタイムに反映されるので直感的。 - 世界中の作品を見られる
他のユーザーが作ったWebアプリやデザインを閲覧でき、学びながら真似することもできます。
初心者がコードの結果をすぐ確認できる点は、ミニコデとよく似ています。そのため、ミニコデで作った作品をそのままブラウザで試すのにも最適です。
CodePenの始め方

右上の「Sign Up」をクリックして、メールアドレスまたはGitHub/Googleアカウントで登録できます。

新しい「Pen(作品)」が開きます。画面上にはHTML・CSS・JSの3つの入力欄が並び、下にプレビューが表示されます。

ミニコデで作ったHTMLやJavaScriptをコピー&ペーストしてみましょう。ほとんどの場合、そのまま動作します。

無料と有料の違い
無料でも十分使えますが、有料プランでは以下のような機能が解放されます。
- 作った作品を非公開にできる
- コラボレーション機能
- 大容量のファイルアップロード
学習目的なら無料プランでまったく問題ありません。
「作って終わり」から「作って見せる」へ──プログラミングの楽しさがさらに広がります。どちらを選ぶかはあなた次第ですが、パソコンを利用するならVSコードの方がオススメです。
登録不要で無料だし、後付けで機能も追加できますからね。
まとめ
ミニコデはスマホひとつでプログラミングを体験できるツールです。
しかし、どうしても「小さな開発向け」に限られています。
ここまで紹介してきた、
- Visual Studio Code(PCで本格開発)
- CodePen(ブラウザで手軽に公開)
上記のツールを使えば、スマホでコードを書いていた経験が最大限活かせるはずです。
ミニコデで培った感覚をそのままに、少しだけ広い世界へ飛び出してみましょう。プログラミングは「環境が変わる」と一気に視野が広がります。
自分の作ったものが他の人に見られたり、チームで動いたり──
それが、次の楽しさへの扉です。
ミニコデで始めた“創る力”を、今度は自由な開発環境で磨いていきましょう。


コメント