乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

プログラミング

今回はアプリをリリースしたので
そちらのご報告です。

解説動画はこちら



アプリについて


今回作成したアプリはこちらです

・チャンクde英会話

iOS
AppStore


Android
Googleplay

良かったら使ってみてください


アプリの作成方法

Flutterを用いて作成しました

Flutterにした理由は
 1.iOSとAndroid両対応であること
 2.学習コストが低い
 3.文献が豊富で有る

以上の理由からFlutterにしてみました。

それ以外にもアプリを作る方法は
いくらでもありますが、今回は
Flutterを使ってみました。

開発言語がDartになるので
1から勉強することになります。

また、コードやデータなどの大半は
ChatGPTを用いて作成していますので
実質ChatGPTに頼れば
アプリの開発は容易かと思います。

学習開発の期間で2ヶ月くらいでした。



リリースについて

コードを実装しシミュレータや
実機でのテストが終わったら
ビルドを行なってアプリを作り
リリース準備ができます。


その前にアプリストアのアカウントがないと
そもそもリリース出来ないので
アプリストアのアカウント取得が必要です。

これもそこそこ手間と時間が掛かります。

リリース登録をしたら審査が行われますが
審査に通らなければビルドからやり直し
審査に通るまでの繰り返しです。

審査に通ったらようやく
リリース、アプリ配信ができる様になります。

リリース作業を始めてから
アカウント登録とアプリリリースまでで
大体1ヶ月くらい掛かっています。



Flutterについて

動画の方では少しだけ解説していますが
FlutterはiOSとAndroidの両方のアプリを
作成する事ができる開発フレームワークです。

Flutterをインストールしたら
VSCodeなどでコードを書き進める事が
できる様になります。

テストやビルドなども
Flutterコマンドを用いて行う形になります。

この辺りも
VSCodeと合わせておくと
開発が楽になるかなと思います。

どんな感じなのかは
動画の方で解説していますので
参考にしていただければと思います。


最後に

これからアプリ開発を行いたい方にとっては
色々な選択肢があると思いますが
Flutterを使ってアプリを開発したい方が
増えていただけたら幸いです。

Python言語の解説と共に
アプリ開発の方も進めていきますので
要望などあれば是非コメントいただければと思います。

それでは。

プログラミング未経験の方のための
プログラミング学習講座を作成しました

その名も
「1時間で学べるPythonプログラミング」


講義動画はこちら




この講座は初学者の方が
短時間でPython言語を学ぶことのできる
プログラミング学習用の講座です

プログラミングが分からないない方は
Python言語を通じて
プログラミングの基礎を学習できます

講座は動画に加えてGoogle Colabを用いて
手元でコードを動かすことのできます
コードがどう動くのかを確認をしながら
進めていってください

資料はここ:
Google Colabの資料


00:00 1.はじめに
02:13 2.導入方法
02:55 3.GoogleColaboratoryの操作方法
06:19 4.Pythonの計算の基礎
27:27 5.Pythonの制御文
42:14 6.Pythonのクラス
49:11 7.Pythonのその他構文
64:30 8.まとめ

なおPythonチートシートを作成しています。

コーディングに迷った際に役に立ち

WEB検索する時間を無くして

作業時間を効率化できます。

note
Pythonチートシート


 

今回は水曜日のダウンタウン企画
電気椅子ゲームのシミュレーションです。


解説動画はこちら

 

電気椅子ゲームとは

水曜日のダウンタウンのネタ
結構色々やっていますが
今回は電気椅子ゲームの
シミュレーションです。

ゲームのルールはこうです。


1~12などの番号が書かれたイス
プレイヤー二人は交互に以下を行う
相手が座るイスを予想し、電流を仕掛ける
電気イスでなければ得点獲得し、イスは撤去される
電流イスに座ると得点没収され、イスはそのまま

最終的にポイントが高い方が勝利
3回電流を喰らうか、40点先取されたら負け

このルールでシミュレーション用の
コードを作ってみました。
import random

headers = ["ターン数", "選択プレイヤー", "選択数字", "電気プレイヤー", "仕掛数字", "結果", "P1得点", "P2得点", "P1電気回数", "P2電気回数", "残り椅子"]

class ElectricChairGame:
    def __init__(self):
        self.chairs = {i: i for i in range(1, 13)}
        self.player_scores = [0, 0]
        self.electric_fails = [0, 0]
        self.current_player = 0
        self.electrified_chair = None

    def choose_chair(self, chair_number):
        # 選択した数字と電気椅子の数字が一致する場合のみ電気椅子確定
        if chair_number == self.electrified_chair:
            self.electric_fails[self.current_player] += 1
            self.player_scores[self.current_player] = 0
            return "電気椅子"
        else:
            points = self.chairs[chair_number]
            self.player_scores[self.current_player] += points
            del self.chairs[chair_number]
            return "得点獲得"

    def set_electric(self, chair_number):
        self.electrified_chair = chair_number
        return True

    def check_game_end(self):
        message = ["", ""]
        for player in range(2):
            if self.electric_fails[player] >= 3:
                winner = 2 if player == 0 else 1
                message = [winner, "電気椅子3回"]
                return True, message
            if self.player_scores[player] >= 40:
                winner = player + 1
                message = [winner, "スコア40"]
                return True, message

        if len(self.chairs) == 1:
            winner = 1 if self.player_scores[0] > self.player_scores[1] else 2
            message = [winner, "スコア差"]
            return True, message

        return False, message

def simulate_game(echo=False):
    game = ElectricChairGame()
    turn,selecting_player = 1,1
    results = []
    while True:
        # 電気椅子設置
        electric_player = 2 if selecting_player == 1 else 1
        electric_choice = random.choice(list(game.chairs.keys()))
        game.set_electric(electric_choice)

        # 椅子選択
        chair_choice = random.choice(list(game.chairs.keys()))
        game.current_player = selecting_player - 1
        result = game.choose_chair(chair_choice)

        # ログ出力
        log_line = [
            turn,
            selecting_player,
            chair_choice,
            electric_player,
            electric_choice,
            result,
            game.player_scores[0],
            game.player_scores[1],
            game.electric_fails[0],
            game.electric_fails[1],
            sorted(list(game.chairs.keys()))
        ]
        results.append(log_line)
        # ゲーム終了判定
        game_end, message = game.check_game_end()
        if game_end:
          if message[0]==1:
              win_score = game.player_scores[0]
          else:
              win_score = game.player_scores[1]
          message+=[turn ,
            win_score,
            game.player_scores[0],
            game.player_scores[1],
            game.electric_fails[0],
            game.electric_fails[1],
            len(list(game.chairs.keys()))]
          if echo:
            print(", ".join(headers))
            for log_line in results:
              print(log_line)
            print(f"ゲーム終了: 勝利プレイヤー : {message[0]}, 勝因 : {message[1]}")
          return message, results

        # プレイヤー交代
        selecting_player = 2 if selecting_player == 1 else 1
        turn += 1

ゲームを実行する場合はこうです
message, log_line = simulate_game(True)
ターン数, 選択プレイヤー, 選択数字, 電気プレイヤー,
仕掛数字, 結果, P1得点, P2得点, P1電気回数, P2電気回数, 残り椅子
[1, 1, 2, 2, 3, '得点獲得', 2, 0, 0, 0, [1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]]
[2, 2, 11, 1, 5, '得点獲得', 2, 11, 0, 0, [1, 3, 4, 5, 6, 7, 8, 9, 10, 12]]
[3, 1, 6, 2, 7, '得点獲得', 8, 11, 0, 0, [1, 3, 4, 5, 7, 8, 9, 10, 12]]
[4, 2, 1, 1, 8, '得点獲得', 8, 12, 0, 0, [3, 4, 5, 7, 8, 9, 10, 12]]
[5, 1, 3, 2, 10, '得点獲得', 11, 12, 0, 0, [4, 5, 7, 8, 9, 10, 12]]
[6, 2, 12, 1, 7, '得点獲得', 11, 24, 0, 0, [4, 5, 7, 8, 9, 10]]
[7, 1, 8, 2, 4, '得点獲得', 19, 24, 0, 0, [4, 5, 7, 9, 10]]
[8, 2, 9, 1, 7, '得点獲得', 19, 33, 0, 0, [4, 5, 7, 10]]
[9, 1, 10, 2, 7, '得点獲得', 29, 33, 0, 0, [4, 5, 7]]
[10, 2, 7, 1, 5, '得点獲得', 29, 40, 0, 0, [4, 5]]
ゲーム終了: 勝利プレイヤー : 2, 勝因 : スコア40





これで1万回やる場合は
こんな感じのコードで
データフレームにできます。
game_result, game_los = [], []
for i in range(10000):
  message, log_line = simulate_game()
  game_result.append([i+1] + message)
  for log in log_line:
    game_los.append([i+1] + log)

import pandas as pd
cols = ["ゲーム数", "勝利プレイヤー", "勝因","ターン数","獲得点数","P1得点", "P2得点", "P1電気回数", "P2電気回数", "残椅子個数"]
game_result_df = pd.DataFrame(game_result, columns=cols)
game_log_df = pd.DataFrame(game_los, columns=["ゲーム数"] + headers)

game_result_df.head()


シミュレーションの結果は
ぜひ動画を見てみてくださいませ


今回は電気椅子ゲームのシミュレーションコードについてでした
それでは!!

今回は映画サマーウォーズの
なつき先輩の誕生日から曜日求める
小ネタです


解説動画はこちら




モジュロ演算

今回はあの映画
「サマーウォーズ」に出てきた
なつき先輩の誕生日から
曜日を求めていたシーンのやつです

あのシーンでは、いわゆる
余り(剰余)を求める計算を行っていました

モジュロ演算とは
単に余りを求める計算のことです。

これとツェラーの公式
がつながります。


ツェラーの公式

西暦(YYYYMMDD)から
曜日を求める公式です。
h=(d+[26(m+1)/10]+Y+[Y/4]-2[y/100]+[y/400])mod 7

こんな感じの計算式ですが

これを使うと余りが
0-6の範囲になり、これが曜日に対応する
ということになります。

0=土曜日
1=日曜日
2=月曜日
3=火曜日
4=水曜日
5=木曜日
6=金曜日


誕生日から曜日を求めるコード


コードはこんな感じです
年月日の変数を変えて貰えば
どの年月日にも対応されます
# モジュロ演算で曜日を求めるコード
def zeller(year, month, day):
    # 1月と2月は前年の13月、14月として扱う
    if month < 3:
        month += 12
        year -= 1

    y = year % 100
    century = year // 100
    
    # ツェラーの公式より
    h = (day + (26 * (month + 1)) // 10 + y + (y // 4) - (2 * century) + (century // 4)) % 7
    
    # 曜日
    # 0=土曜日, 1=日曜日, 2=月曜日, 3=火曜日, 4=水曜日, 5=木曜日, 6=金曜日
    return h

weekdays = ["土曜日", "日曜日", "月曜日", "火曜日", "水曜日", "木曜日", "金曜日"]

# 使用例
year, month, day = 2025, 2, 1

h = zeller(year, month, day)
weekday_name = weekdays[h]
print(f"{year}年{month}月{day}日は{weekday_name}です。")
2025年2月1日は土曜日です。


なつき先輩の誕生日の曜日はなんだったか?

なつき先輩の誕生日は
1992年7月19日
となっています

コードを使って求めると

日曜日

だそうです。

当たっていましたかね?



余り使う機会のない
小ネタですが
どこかで使われることがあったら
嬉しいかもしれないですね

今日はここまでです

それでは
 

今回もJSでクソゲーを作ってみました


解説動画はこちら







クソゲー作ってみた

その名も
「ずっこんテトリス」
です

普通のテトリスを改変して
少し面白くなるようにしてみました
こちらからプレイできます。

ずっこんテトリス

上下左右キーの操作のみの
シンプル設計で

長いテトリス棒を追加しました
sample


こやつは「ずっこんテトリス棒」ですね

コードはこんな感じになっています。
const game = document.getElementById("game");
const rareChanceInput = document.getElementById("rareChance");
const chanceValueDisplay = document.getElementById("chanceValue");
const ROWS = 20;
const COLS = 10;

// グリッドを初期化
const grid = Array.from({ length: ROWS }, () => Array(COLS).fill(0));

// 通常のテトリミノ
const tetrominoes = [
  [[1, 1, 1, 1]], // I
  [
    [1, 1],
    [1, 1],
  ], // O
  [
    [0, 1, 0],
    [1, 1, 1],
  ], // T
  [
    [1, 1, 0],
    [0, 1, 1],
  ], // S
  [
    [0, 1, 1],
    [1, 1, 0],
  ], // Z
  [
    [1, 1, 1],
    [1, 0, 0],
  ], // L
  [
    [1, 1, 1],
    [0, 0, 1],
  ], // J
];

// レアな「ロング棒」長さを8に変更
const rareTetromino = [
  [[1, 1, 1, 1, 1, 1, 1, 1]], // 長さ8の棒
];

// テトリミノの状態
let currentTetromino = getRandomTetromino();
let currentRow = 0;
let currentCol = Math.floor((COLS - currentTetromino[0].length) / 2);

// グリッドを描画
function drawGrid() {
  game.innerHTML = "";
  for (let row = 0; row < ROWS; row++) {
    for (let col = 0; col < COLS; col++) {
      const cell = document.createElement("div");
      cell.classList.add("cell");
      if (grid[row][col] === 1) {
        cell.classList.add("filled");
      }
      game.appendChild(cell);
    }
  }
}

// テトリミノを描画
function drawTetromino() {
  currentTetromino.forEach((row, r) => {
    row.forEach((value, c) => {
      if (value && currentRow + r >= 0) {
        grid[currentRow + r][currentCol + c] = 1;
      }
    });
  });
}

// テトリミノを削除
function clearTetromino() {
  currentTetromino.forEach((row, r) => {
    row.forEach((value, c) => {
      if (value && currentRow + r >= 0) {
        grid[currentRow + r][currentCol + c] = 0;
      }
    });
  });
}

// 衝突判定
function isValidMove(newRow, newCol, newTetromino) {
  return newTetromino.every((row, r) =>
    row.every((value, c) => {
      const x = newCol + c;
      const y = newRow + r;
      return (
        !value ||
        (y >= 0 && y < ROWS && x >= 0 && x < COLS && grid[y][x] === 0)
      );
    })
  );
}

// ラインを削除
function clearLines() {
  for (let row = ROWS - 1; row >= 0; row--) {
    if (grid[row].every((cell) => cell === 1)) {
      grid.splice(row, 1);
      grid.unshift(Array(COLS).fill(0));
      row++;
    }
  }
}

// ランダムなテトリミノを取得(レア形状の低確率出現を含む)
function getRandomTetromino() {
  const rareChance = parseFloat(rareChanceInput.value); // スライダーの値を取得
  if (Math.random() < rareChance) { // ロング棒の出現確率
    return rareTetromino[0];
  } else {
    return tetrominoes[Math.floor(Math.random() * tetrominoes.length)];
  }
}

// テトリミノを回転
function rotateTetromino() {
  const newTetromino = currentTetromino[0].map((_, colIndex) =>
    currentTetromino.map((row) => row[colIndex]).reverse()
  );

  if (isValidMove(currentRow, currentCol, newTetromino)) {
    currentTetromino = newTetromino;
  }
}

// ゲームのループ
function gameLoop() {
  clearTetromino();
  if (isValidMove(currentRow + 1, currentCol, currentTetromino)) {
    currentRow++;
  } else {
    drawTetromino();
    clearLines();

    // 次のテトリミノを生成
    currentTetromino = getRandomTetromino();
    currentRow = 0;
    currentCol = Math.floor((COLS - currentTetromino[0].length) / 2);

    // ゲームオーバー判定
    if (!isValidMove(currentRow, currentCol, currentTetromino)) {
      alert("Game Over");
      grid.forEach((row) => row.fill(0));
      currentTetromino = getRandomTetromino();
      currentRow = 0;
      currentCol = Math.floor((COLS - currentTetromino[0].length) / 2);
    }
  }
  drawTetromino();
  drawGrid();
}

// キー操作
document.addEventListener("keydown", (e) => {
  clearTetromino();
  if (e.key === "ArrowLeft" && isValidMove(currentRow, currentCol - 1, currentTetromino)) {
    currentCol--;
  } else if (e.key === "ArrowRight" && isValidMove(currentRow, currentCol + 1, currentTetromino)) {
    currentCol++;
  } else if (e.key === "ArrowDown") {
    if (isValidMove(currentRow + 1, currentCol, currentTetromino)) {
      currentRow++;
    }
  } else if (e.key === "ArrowUp") {
    rotateTetromino();
  }
  drawTetromino();
  drawGrid();
});

// スライダーの値を表示
rareChanceInput.addEventListener("input", () => {
  chanceValueDisplay.textContent = parseFloat(rareChanceInput.value).toFixed(2);
});

// ゲーム開始
setInterval(gameLoop, 500);
drawGrid();


ここでレアなテトリス棒として定義し
出現確率を画面上のスライダーの値で設定しています。

ここを上げると、出現確率も上がります。

どの確率が一番面白くなるかは分かりませんので
色々変えて試していただければと思います。

コードも改変すると
面白くなるかもしれないので
色々遊んでみてください

それでは






 

このページのトップヘ