①を読んでない人はこちら
1 DB関連
db_connect.phpを使用して、DBとの接続を管理
db_connect.php
<?php
// .env読み込み関数
function loadEnv($path) {
if (!file_exists($path)) {
throw new Exception('.env file not found');
}
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (strpos($line, '=') !== false && strpos($line, '#') !== 0) {
list($key, $value) = explode('=', $line, 2);
$_ENV[trim($key)] = trim($value);
}
}
}
// .envファイルを読み込み
loadEnv('sec.env');
// 変数で定義(定数の代わり)
$PDO_DSN = "mysql:host={$_ENV['DB_HOST']};dbname={$_ENV['DB_NAME']};charset={$_ENV['DB_CHARSET']}";
$USERNAME = $_ENV['DB_USER'];
$PASSWORD = $_ENV['DB_PASS'];
// データベース接続
function connectDB() {
global $PDO_DSN, $USERNAME, $PASSWORD;
try {
$pdo = new PDO($PDO_DSN, $USERNAME, $PASSWORD);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (PDOException $e) {
die("データベース接続エラー: " . $e->getMessage());
}
}
$pdo = connectDB();
?>
sec.env
DB_HOST=localhost
DB_NAME=
DB_USER=
DB_PASS=
DB_CHARSET=utf8mb4
2.1 ログイン画面
login.php
<?php
session_start();
// データベース接続ファイルを読み込み
require_once 'db_connect.php';
$error_message = '';
$success_message='';
// フォームが送信された場合の処理
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = trim($_POST['name']);
//パスワード
$pin = $_POST['pin'];
//アクション(ログインor登録)
$action = $_POST['action'];
// バリデーション
if (empty($name) || empty($pin)) {
$error_message = '名前とパスワードを入力してください。';
} elseif (!preg_match('/^\d{4}$/', $pin)) {
$error_message = 'パスワードは4桁の数字で入力してください。';
} else {
if ($action === 'login') {
// ログイン処理
try {
$stmt = $pdo->prepare("SELECT * FROM status WHERE name = ? AND pin = ?");
$stmt->execute([$name, $pin]);
$user = $stmt->fetch();
} catch (PDOException $e) {
$error_message = 'データベースエラーが発生しました。';
// ログに記録(本番環境では)
error_log($e->getMessage());
}
if ($user) {
$_SESSION['id'] = $user['id'];
$_SESSION['name'] = $user['name'];
$_SESSION['winRate'] = $user['winRate'];
$_SESSION['drawRate'] = $user['drawRate'];
$_SESSION['loseRate'] = $user['loseRate'];
$_SESSION['handCount'] = $user['handCount'];
$_SESSION['guuRatio'] = $user['guuRatio'];
$_SESSION['chokiRatio'] = $user['chokiRatio'];
$_SESSION['paaRatio'] = $user['paaRatio'];
$_SESSION['point'] = $user['point'];
header('Location: index.php');
exit;
} else {
$error_message = 'ユーザー名またはパスワードが間違っています。';
}
} elseif ($action === 'register') {
// 登録処理
// まず同じ名前のユーザーが存在するかチェック
$stmt = $pdo->prepare("SELECT COUNT(*) FROM status WHERE name = ?");
$stmt->execute([$name]);
$count = $stmt->fetchColumn();
if ($count > 0) {
$error_message = 'この名前は既に使用されています。';
} else {
// 新規ユーザー登録(全カラムに初期値を設定)
$stmt = $pdo->prepare("INSERT INTO status (name, pin, winRate, drawRate, loseRate, handCount, guuRatio, chokiRatio, paaRatio, point) VALUES (?, ?, 0, 0, 0, 0, 0, 0, 0, 0)");
if ($stmt->execute([$name, $pin])) {
$user_id = $pdo->lastInsertId();
$_SESSION['id'] = $user_id;
$_SESSION['name'] = $name;
$_SESSION['winRate'] = 0;
$_SESSION['drawRate'] = 0;
$_SESSION['loseRate'] = 0;
$_SESSION['handCount'] = 0;
$_SESSION['guuRatio'] = 0;
$_SESSION['chokiRatio'] = 0;
$_SESSION['paaRatio'] = 0;
$_SESSION['point'] = 1000;
header('Location: index.php');
exit;
} else {
$error_message = '登録に失敗しました。';
}
}
}
}
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>じゃんけんギャンブル - ログイン</title>
<link rel="stylesheet" href="style_login.css">
</head>
<body>
<div class="login-container">
<h1 class="game-title">🎲 じゃんけんギャンブル 🎲</h1>
<p class="subtitle">運試しの時間だ!</p>
<?php if ($error_message): ?>
<div class="error-message"><?php echo htmlspecialchars($error_message); ?></div>
<?php endif; ?>
<?php if ($success_message): ?>
<div class="success-message"><?php echo htmlspecialchars($success_message); ?></div>
<?php endif; ?>
<form method="POST" action="">
<div class="form-group">
<label for="name">プレイヤー名</label>
<input type="text" id="name" name="name" placeholder="あなたの名前を入力"
value="<?php echo isset($_POST['name']) ? htmlspecialchars($_POST['name']) : ''; ?>" required>
</div>
<div class="form-group">
<label for="pin">パスワード(4桁の数字)</label>
<input type="password" id="pin" name="pin" placeholder="1234"
pattern="\d{4}" maxlength="4" required>
</div>
<div class="button-group">
<button type="submit" name="action" value="login" class="login-btn">
ログイン
</button>
<button type="submit" name="action" value="register" class="register-btn">
新規登録
</button>
</div>
</form>
<div class="game-info">
<p><strong>ゲーム概要</strong></p>
<p><span class="emoji">✊</span><span class="emoji">✋</span><span class="emoji">✌️</span></p>
<p>じゃんけんでコインを賭けて勝負!<br>
</p>
</div>
</div>
<script>
// 数字のみ入力を許可
document.getElementById('pin').addEventListener('input', function(e) {
this.value = this.value.replace(/[^0-9]/g, '');
});
// フォーム送信時の確認
document.querySelector('form').addEventListener('submit', function(e) {
const name = document.getElementById('name').value.trim();
const pin = document.getElementById('pin').value;
const action = e.submitter.value;
if (!name || !pin) {
alert('名前とパスワードを入力してください。');
e.preventDefault();
return;
}
if (!/^\d{4}$/.test(pin)) {
alert('パスワードは4桁の数字で入力してください。');
e.preventDefault();
return;
}
if (action === 'register') {
if (!confirm('新しいアカウントを作成しますか?')) {
e.preventDefault();
}
}
});
</script>
</body>
</html>
style_login.css
body {
font-family: 'Arial', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
margin: 0;
padding: 0;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.login-container {
background: white;
padding: 40px;
border-radius: 15px;
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2);
width: 100%;
max-width: 400px;
text-align: center;
}
.game-title {
color: #333;
font-size: 28px;
margin-bottom: 10px;
font-weight: bold;
}
.subtitle {
color: #666;
font-size: 16px;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
text-align: left;
}
label {
display: block;
margin-bottom: 8px;
color: #333;
font-weight: bold;
}
input[type="text"], input[type="password"] {
width: 100%;
padding: 12px;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 16px;
box-sizing: border-box;
transition: border-color 0.3s;
}
input[type="text"]:focus, input[type="password"]:focus {
outline: none;
border-color: #667eea;
}
.button-group {
display: flex;
gap: 15px;
margin-top: 30px;
}
button {
flex: 1;
padding: 12px 20px;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
}
.login-btn {
background: #667eea;
color: white;
}
.login-btn:hover {
background: #5a6fd8;
transform: translateY(-2px);
}
.register-btn {
background: #48bb78;
color: white;
}
.register-btn:hover {
background: #38a169;
transform: translateY(-2px);
}
.error-message {
background: #fed7d7;
color: #c53030;
padding: 12px;
border-radius: 8px;
margin-bottom: 20px;
border-left: 4px solid #c53030;
}
.success-message {
background: #c6f6d5;
color: #2f855a;
padding: 12px;
border-radius: 8px;
margin-bottom: 20px;
border-left: 4px solid #2f855a;
}
.game-info {
background: #f7fafc;
padding: 20px;
border-radius: 8px;
margin-top: 30px;
color: #666;
font-size: 14px;
}
.emoji {
font-size: 24px;
margin: 0 5px;
}
2.2 メイン画面
index.php
<?php
// セッションの開始
session_start();
// データベース接続ファイルを読み込み
require_once 'db_connect.php';
// ユーザーをデータベースに保存する関数
function saveUserToDB($username) {
// $pdo = connectDB();
// 既に同じ名前のユーザーが存在するかチェック
$stmt = $pdo->prepare("SELECT id FROM status WHERE name = ?");
$stmt->execute([$username]);
if ($stmt->rowCount() == 0) {
// 新規ユーザーの場合、データベースに保存(全フィールドに初期値を設定)
$stmt = $pdo->prepare("
INSERT INTO status (
name, winRate, drawRate, loseRate, handCount,
guuRatio, chokiRatio, paaRatio, point
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$username, // name
0, // winRate (初期勝率0%)
0, // drawRate (初期引き分け率0%)
0, // loseRate (初期負け率0%)
0, // handCount (初期手数0)
0, // gu-Ratio (初期グー比率0%)
0, // chokiRatio (初期チョキ比率0%)
0, // pa-Ratio (初期パー比率0%)
1000 // point (初期ポイント1000)
]);
return $pdo->lastInsertId(); // 新しく作成されたIDを返す
} else {
// 既存ユーザーの場合、IDを取得
$user = $stmt->fetch(PDO::FETCH_ASSOC);
return $user['id'];
}
}
// ページ遷移の処理
if (isset($_POST['action'])) {
switch ($_POST['action']) {
case 'game':
if (!isset($_SESSION['name'])) {
header('Location: login.php');
exit();
} else {
header('Location: game.php');
exit();
}
break;
case 'ranking':
header('Location: ranking.php');
exit();
break;
case 'status':
if (!isset($_SESSION['name'])) {
header('Location: login.php');
exit();
} else {
header('Location: status.php');
exit();
}
break;
case 'logout':
// セッションの完全削除
$_SESSION = [];
setcookie(session_name(), '', time() - 1, '/');
session_destroy();
header('Location: login.php');
exit();
break;
}
}
// IDチェックとプレイヤー情報の取得
if (!isset($_SESSION['id'])) {
header('Location: login.php');
exit();
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>じゃんけんギャンブル</title>
<link rel="stylesheet" href="style_index.css">
</head>
<body>
<div class="container">
<h1>🎲 じゃんけんギャンブル 🎲</h1>
<div class="odds-info">
<h3>📊 オッズ情報</h3>
<div class="odds-table">
<div class="odds-item">
<strong>✂️ チョキ</strong><br>
勝利: 2倍<br>
敗北: 2倍
</div>
<div class="odds-item">
<strong>👊 グー</strong><br>
勝利: 1.5倍<br>
敗北: 1.5倍
</div>
<div class="odds-item">
<strong>✋ パー</strong><br>
勝利: 5倍<br>
敗北: 5倍
</div>
</div>
</div>
<?php if (isset($_SESSION['name'])): ?>
<div class="user-info">
<h3>👤 ユーザー情報</h3>
<p><strong>ID:</strong> <?= htmlspecialchars($_SESSION['id'] ?? 'N/A') ?></p>
<p><strong>名前:</strong> <?= htmlspecialchars($_SESSION['name']) ?></p>
<p><strong>所持ポイント:</strong> <?= number_format($_SESSION['point'] ?? 1000) ?> pt</p>
</div>
<?php endif; ?>
<?php if (isset($error_msg)): ?>
<div class="error">
⚠️ <?= htmlspecialchars($error_msg) ?>
</div>
<?php endif; ?>
<div class="navigation">
<form method="POST" style="margin: 0;">
<input type="hidden" name="action" value="game">
<button type="submit">🎮 ゲーム画面</button>
</form>
<form method="POST" style="margin: 0;">
<input type="hidden" name="action" value="status">
<button type="submit">📊 ステータス</button>
</form>
<form method="POST" style="margin: 0;">
<input type="hidden" name="action" value="ranking">
<button type="submit">🏆 ランキング</button>
</form>
<?php if (isset($_SESSION['name'])): ?>
<form method="POST" style="margin: 0;">
<input type="hidden" name="action" value="logout">
<button type="submit" class="reset-btn" onclick="return confirm('本当にログアウトしますか?')">👋 ログアウト</button>
</form>
<?php endif; ?>
</div>
<div style="margin-top: 30px; padding: 20px; background-color: #f0f0f0; border-radius: 5px;">
<h3>📝 ゲームルール</h3>
<ul>
<li>初期ポイント: 1000pt</li>
<li>じゃんけんで勝負し、賭けたポイントがオッズに応じて増減します</li>
<li>勝てば賭けたポイント × オッズ分のポイントを獲得します</li>
<li>負ければ賭けたポイント × オッズ分のポイントを失います</li>
<li>あいこの場合は賭けたポイントはそのまま返却</li>
</ul>
</div>
</div>
</body>
</html>
style_index.css
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background-color: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
text-align: center;
border-bottom: 3px solid #4CAF50;
padding-bottom: 10px;
}
.odds-info {
background-color: #e8f5e8;
padding: 15px;
border-radius: 5px;
margin: 20px 0;
}
.odds-info h3 {
margin-top: 0;
color: #2e7d32;
}
.odds-table {
display: flex;
justify-content: space-around;
text-align: center;
}
.odds-item {
flex: 1;
padding: 10px;
}
.form-section {
background-color: #f9f9f9;
padding: 20px;
border-radius: 5px;
margin: 20px 0;
}
.user-info {
background-color: #e3f2fd;
padding: 15px;
border-radius: 5px;
margin: 20px 0;
}
.error {
background-color: #ffebee;
color: #c62828;
padding: 10px;
border-radius: 5px;
margin: 10px 0;
border-left: 4px solid #c62828;
}
.navigation {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 10px;
margin: 20px 0;
}
button, input[type="submit"] {
background-color: #4CAF50;
color: white;
padding: 12px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
}
button:hover, input[type="submit"]:hover {
background-color: #45a049;
}
.reset-btn {
background-color: #f44336;
}
.reset-btn:hover {
background-color: #da190b;
}
input[type="text"] {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 5px;
font-size: 16px;
margin-top: 5px;
}
input[type="text"]:focus {
border-color: #4CAF50;
outline: none;
}
label {
font-weight: bold;
color: #333;
}
small {
color: #666;
}

