OSDN Git Service

[add]スコアランキング表示用コード
authorHabu <habu@users.sourceforge.jp>
Sat, 17 Mar 2018 02:03:48 +0000 (11:03 +0900)
committerHabu <habu@users.sourceforge.jp>
Sat, 17 Mar 2018 02:03:48 +0000 (11:03 +0900)
db_common.inc
score_ranking.php [new file with mode: 0644]

index ef5e2bb..44c99d9 100644 (file)
@@ -1,9 +1,160 @@
 <?php
 class ScoreDB
 {
+    private static $sort_mode_list = ['default' => 'score', 'newcome'];
+
     public function __construct() {
         $this->dbh = new PDO('sqlite:db/score.db');
         $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+        $this->set_sort_mode(filter_input(INPUT_GET, 'sort'));
+    }
+
+    /**
+     * スコア表示モードを設定する
+     *
+     * @param string $mode 設定する表示モード
+     * 'score' - スコア順に表示(デフォルト)
+     * 'newcome' - 新着順に表示
+     * 存在しない表示モードが指定された場合デフォルトの表示モードが設定される
+     */
+    public function set_sort_mode($mode)
+    {
+        if ($mode !== NULL && in_array($mode, self::$sort_mode_list)) {
+            $this->sort_mode = $mode;
+        } else {
+            $this->sort_mode = self::$sort_mode_list['default'];
+        }
+    }
+
+
+    /**
+     * スコア表示モード名を取得する
+     *
+     * @return string スコア表示モード名
+     */
+    public function get_sort_mode_name()
+    {
+        switch ($this->sort_mode) {
+        case 'score':
+            return "スコア順";
+        case 'newcome':
+            return "新着順";
+        default:
+            return "不明";
+        }
+    }
+
+
+    /**
+     * スコア検索の絞り込み用WHERE句を取得する
+     *
+     * @return string スコア検索絞り込み用のWHERE句
+     */
+    private function get_where()
+    {
+        $last_days = filter_input(INPUT_GET, 'last_days', FILTER_VALIDATE_INT);
+        if ($last_days > 0) {
+            $wheres[] = "date >= datetime('now', 'localtime', '-{$last_days} days')";
+        }
+
+        foreach (['race_id', 'class_id', 'personality_id'] as $id_str) {
+            $id = filter_input(INPUT_GET, $id_str, FILTER_VALIDATE_INT); 
+            if ($id > 0) {
+                $wheres[] = "{$id_str} = {$id}";
+            }
+        }
+
+        $where = isset($wheres) ? 'WHERE '.implode(' AND ', $wheres) : '';
+
+        return $where;
+    }
+
+
+    /**
+     * スコアソート用のORDER BY句を取得する
+     *
+     * @return string スコアソート用のORDER BY句
+     */
+    private function get_order_by()
+    {
+        switch ($this->sort_mode) {
+        case "score":
+            $order_by = "ORDER BY score DESC";
+            break;
+        case "newcome":
+            $order_by = 'ORDER BY score_id DESC';
+            break;
+        }
+
+        return $order_by;
+    }
+
+
+    /**
+     * スコア検索用のSQLクエリを取得する
+     *
+     * @param integer $offset スコア取得開始位置
+     * @param integer $limit スコア取得最大件数
+     * @param string $where スコア検索に用いるWHERE句
+     * @param string $order_by スコアソートに用いるORDER BY句
+     * @return string スコア検索用SQLクエリ
+     */
+    private function get_search_query($offset, $limit, $where, $order_by)
+    {
+        switch ($this->sort_mode) {
+        case "score":
+            $query = "SELECT *, group_concat(realm_name) AS realms_name from (select * from scores ${where} {$order_by} LIMIT ${offset}, {$limit}) NATURAL JOIN races NATURAL JOIN classes NATURAL JOIN personalities NATURAL LEFT JOIN score_realms NATURAL LEFT JOIN realms GROUP BY score_id {$order_by}";
+            break;
+        case "newcome":
+            $query = "SELECT *, group_concat(realm_name) AS realms_name from (select * from scores ${where} {$order_by} LIMIT ${offset}, {$limit}) NATURAL JOIN races NATURAL JOIN classes NATURAL JOIN personalities NATURAL LEFT JOIN score_realms NATURAL LEFT JOIN realms GROUP BY score_id {$order_by}";
+            break;
+        }
+
+        return $query;
+    }
+
+
+    /**
+     * 検索でヒットしたスコアの総件数を取得する
+     *
+     * @param string $where スコア検索に用いるWHERE句
+     * @return integer スコア総件数
+     */
+    private function get_query_data_count($where)
+    {
+        $stmt = $this->dbh->query("SELECT count(*) AS data_count from scores {$where}");
+        $res = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+        return intval($res[0]['data_count']);
+    }
+
+
+    /**
+     * スコアを検索する
+     *
+     * @param integer $start_num 検索するスコアの開始位置
+     * @param integer $count 検索するスコア数
+     *
+     * @return array 検索したスコアの配列と、条件に合致するスコアの総件数の連想配列
+     */
+    public function search_score($start_num, $count)
+    {
+        $where = $this->get_where();
+        $order_by = $this->get_order_by();
+        $query_sql = $this->get_search_query(intval($start_num / $count) * $count, $count, $where, $order_by);
+
+        $search_start_time = microtime(true);
+        $this->dbh->beginTransaction();
+
+        $score_stmt = $this->dbh->query($query_sql);
+        $result['scores'] = $score_stmt->fetchAll(PDO::FETCH_ASSOC);
+        $result['total_data_count'] = $this->get_query_data_count($where);
+
+        $this->dbh->commit();
+        $result['elapsed_time'] = microtime(true) - $search_start_time;
+
+        return $result;
     }
 
     public function get_db_handle() {
diff --git a/score_ranking.php b/score_ranking.php
new file mode 100644 (file)
index 0000000..6117bb6
--- /dev/null
@@ -0,0 +1,190 @@
+<?php
+//ini_set('display_errors', 'On');
+
+ini_set('log_errors', 'On');
+ini_set('error_log', 'errors/'.pathinfo(__FILE__, PATHINFO_FILENAME).'.log');
+
+include "db_common.inc";
+include "dump_file.inc";
+
+ini_set('zlib.output_compression', 'On');
+
+function get_death_reason($killer)
+{
+    switch ($killer) {
+    case "ripe":
+        return "勝利の後引退";
+    case "Seppuku":
+        return "勝利の後切腹";
+    default:
+        return $killer."に殺された";
+    }
+}
+
+
+/**
+ * ページ情報を計算する
+ *
+ * @param integer $total_data_count 全データ件数
+ * @param integer $start_num GETパラメータで渡された開始データ番号
+ * @param integer $data_count_per_page 1ページあたりのデータ件数
+ *
+ * @return array 計算したページ情報を保持する連想配列
+ */
+function calc_page_info($total_data_count, $start_num, $data_count_per_page)
+{
+    $current_page = intval($start_num / $data_count_per_page);
+    $last_page = intval(($total_data_count - 1) / $data_count_per_page);
+    $navi_page_range_start = ($current_page - 5) > 0 ? $current_page - 5: 0;
+    $navi_page_range_count = max(0, min(9, $last_page - $navi_page_range_start));
+    $navi_page_list = range($navi_page_range_start, $navi_page_range_start + $navi_page_range_count);
+
+    $pageinfo['current'] = $current_page;
+    $pageinfo['last'] = $last_page;
+    $pageinfo['navi_list'] = $navi_page_list;
+
+    $pageinfo['total_data_count'] = $total_data_count;
+    $pageinfo['data_count_per_page'] = $data_count_per_page;
+
+    return $pageinfo;
+}
+
+
+/**
+ * ページナビゲーションテーブルを表示する
+ *
+ * @param array $pageinfo calc_page_info()関数で取得したページ情報を保持する連想配列
+ */
+function print_navi_page_table($pageinfo)
+{
+    if (count($pageinfo['navi_list']) <= 1) return;
+
+    $href_base = preg_replace('/(&?start=\w+)/', '', filter_input(INPUT_SERVER, 'REQUEST_URI'));
+    if (strpos($href_base, "?") === FALSE) {
+        $href_base .= "?";
+    }
+
+    echo "<table align='center'>\n"
+        ."<tr>";
+
+    if ($pageinfo['current'] > 0) {
+        $href = $href_base . "&start=". ($pageinfo['current'] - 1) * $pageinfo['data_count_per_page'];
+        echo "<td><a href={$href}>&lt; 前へ</a></td>";
+    }
+
+    foreach ($pageinfo['navi_list'] as $page) {
+        $page_num = $page + 1;
+        $href = $href_base . "&start=". $page * $pageinfo['data_count_per_page'];
+        if ($page === $pageinfo['current']) {
+            echo "<td>$page_num</td>";
+        } else {
+            echo "<td><a href={$href}>$page_num</a></td>";
+        }
+    }
+
+    if ($pageinfo['current'] < $pageinfo['last']) {
+        $href = $href_base . "&start=". ($pageinfo['current'] + 1) * $pageinfo['data_count_per_page'];
+        echo "<td><a href={$href}>次へ &gt;</a></td>";
+    }
+
+    echo "</tr>\n"
+        ."</table>\n";
+}
+
+
+/**
+ * スコアランキングテーブルを表示する
+ *
+ * @param array $scores スコア
+ * @param integer $rank_start 順位の開始番号(0オリジン)
+ */
+function print_score_table($scores, $rank_start)
+{
+    echo <<<EOM
+<table align='center' border=1>
+<tr>
+<th>順位</th>
+<th>スコア</th>
+<th>日付</th>
+<th>名前</th>
+<th>種族</th>
+<th>職業</th>
+<th>性別</th>
+<th>死因</th>
+</tr>
+
+EOM;
+
+    foreach($scores as $idx => $score) {
+        //$rank = $pageinfo['current'] * $pageinfo['data_count_per_page'] + $idx + 1;
+        $rank = $rank_start + $idx + 1;
+        $score_id = $score['score_id'];
+        $date = substr($score['date'], 0, 10);
+        $sex_str = $score['sex'] ? "男" : "女";
+        $death_reason = get_death_reason($score['killer']);
+        $depth = !$score['winner'] ? $score['depth']."階, " : "";
+        $realms = isset($score['realms_name']) ? "(".$score['realms_name'].")" : "";
+        $dumpfile = new DumpFile($score_id);
+
+        echo "<tr>\n";
+        if ($dumpfile->exists('dumps', 'txt')) {
+            echo "<td><a href=\"show_dump.php?score_id={$score_id}\">{$rank}</a></td>\n";
+        } else {
+            echo "<td>{$rank}</td>\n";
+        }
+        echo <<<EOM
+<td align="right">{$score['score']}</td>
+<td><nobr>$date</nobr></td>
+<td>{$score['personality_name']}{$score['name']}</td>
+<td>{$score['race_name']}</td>
+<td>{$score['class_name']}$realms</td>
+<td>$sex_str</td>
+
+EOM;
+        if ($dumpfile->exists('screens', 'html')) {
+            echo "<td><a href=\"show_screen.php?score_id={$score_id}\">{$death_reason}</a>";
+        } else {
+            echo "<td>{$death_reason}";
+        }
+        echo "<br>({$depth}{$score['version']})</td>\n";
+        echo "</tr>\n";
+    }
+    echo "</table>\n";
+}
+
+$db = new ScoreDB();
+
+$start_num = filter_input(INPUT_GET, 'start', FILTER_VALIDATE_INT) ?: 0;
+$search_result = $db->search_score($start_num, 50);
+
+$pageinfo = calc_page_info($search_result['total_data_count'], $start_num, 50);
+?>
+
+<!DOCTYPE html>
+
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="ROBOTS" content="NOINDEX, NOFOLLOW">
+<title>変愚蛮怒 スコア ランキング</title>
+<link rel="alternate" title="変愚蛮怒 新着スコア" href="html/newcome-rss.xml" type="application/rss+xml">
+</head>
+
+<body>
+<h1>変愚蛮怒 歴代スコア (<?php echo $db->get_sort_mode_name(); ?>)</h1>
+<!--3日以内のスコアは<font color=red>赤</font>、10日以内のスコアは<font color=blue>青</font>で表示されます。<br>-->
+<!--10日以内のスコアは<strong>強調表示</strong>されます。-->
+<!--<br><a href ="html/newcome-rss.xml">新着チェック用RSS</a><small>…スコア受信時に自動生成します。URLをRSSリーダー等に登録すると新着スコアをチェックできます。</small>-->
+<small>
+<?php
+echo sprintf("件数 %d 件 (%.2f 秒)", $search_result['total_data_count'], $search_result['elapsed_time']);
+?>
+</small>
+
+<hr>
+
+<?php
+print_navi_page_table($pageinfo);
+print_score_table($search_result['scores'], $pageinfo['current'] * $pageinfo['data_count_per_page']);
+print_navi_page_table($pageinfo);
+?>