Skip to content

Instantly share code, notes, and snippets.

@srea
Created April 12, 2017 13:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save srea/918655d0ebc9f706e83e4d8241f498bb to your computer and use it in GitHub Desktop.
Save srea/918655d0ebc9f706e83e4d8241f498bb to your computer and use it in GitHub Desktop.
パズドラ風PHP
<?php
// 一列ごとにどこからどこに動かせが揃うかを調査
// 一番いいところを見つけたら次はそこからどこに
// 動かせば一番そろうか調査
// ループして一番いい条件を出す。
// 2012年4月6日
// ・マップ上で消せそうな物を探す。
// ・どれを探すか優先順位を決定する
// (青優先・赤優先・回復優先等)
// ・優先順位が決まったらそのコンボが揃うように動かす
// ・
// tmp
// 0マッチしない
// 1縦にマッチ
// 2横にマッチ
// 3縦横にマッチ
$data = array();
$data[] = array(
6,
1,
2,
3,
4,
5
);
$data[] = array(
1,
2,
3,
4,
5,
6
);
$data[] = array(
1,
2,
3,
4,
5,
6
);
$data[] = array(
1,
2,
3,
4,
5,
6
);
$data[] = array(
1,
2,
3,
4,
5,
6
);
//P::set($data);
P::set();
P::get();
class PazzleException extends Exception
{
}
class Escape
{
}
class Candy
{
}
class Board
{
}
// パズドラ解析
class P
{
// マップ設定
const X = 6; // 横の数
const Y = 5; // 縦の数
const V = 1; // 縦のマッチ
const H = 2; // 横のマッチ
const M = 3; // 両方マッチ
// ダンプ時の色設定
const COL_BASE = "\033[0;%dm"; // 白
const COL_0 = "0"; // 白
const COL_1 = "31"; // 赤
const COL_2 = "34"; // 青
const COL_3 = "32"; // 緑
const COL_4 = "33"; // 黄
const COL_5 = "35"; // 紫
const COL_6 = "36"; // 回復
const COL_X = "45"; // 指の位置
const ITEM_MIN = 1; // アイテム最小値
const ITEM_MAX = 6; // アイテム最大値
const MOVE_MAX = 200; // 最大移動回数
static $data = array(); // マップデータ
static $tmp = array(); // 処理データ
static $combo = array(); // コンボデータ
static $finger = array(); // 現在の指の位置
static $comboCount = 0; // 現在の指の位置
static $nextMove = 0; // 現在の指の位置
static $cnt = 0; //つかってない
static $movePattern = // 指の移動パターン
array(1 => array(-1, 0), // 上
2 => array(1, 0), // 下
3 => array(0, 1), // 右
4 => array(0, -1), // 左
5 => array(-1, 1), // 左上
6 => array(1, 1), // 左下
7 => array(-1, -1), // 右上
8 => array(1, -1) // 右下
);
static $checkPattern = // コンボ判定パターン
array(1 => array(-1, 0), // 上
2 => array(1, 0), // 下
3 => array(0, 1), // 右
4 => array(0, -1) // 左
);
// マップデータをチェック
// 縦横の幅、アイテム数値をチェックします。
static private function check()
{
// 縦幅
if (count(self::$data) != self::Y)
return false;
// 横幅
for ($i = 0; $i < self::Y; $i++) {
if (count(self::$data[$i]) != self::X)
return false;
for ($j = 0; $j < self::X; $j++) {
if (!(self::$data[$i][$j] >= self::ITEM_MIN && self::$data[$i][$j] <= self::ITEM_MAX)) {
return false;
}
}
}
return true;
}
// ランダムマップを生成
static private function createMap()
{
self::$data = array_fill(0, self::Y, array_fill(0, self::X, 0));
self::$tmp = self::$data;
for ($i = 0; $i < self::Y; $i++) {
for ($j = 0; $j < self::X; $j++) {
self::$data[$i][$j] = rand(self::ITEM_MIN, self::ITEM_MAX);
}
}
}
// マップデータをセット
static public function set($data = array())
{
// マップ生成
if (empty($data)) {
self::createMap();
} else {
self::$data = $data;
}
if (!self::check()) {
die("Map erorr");
}
self::$cnt = count($data);
self::$finger = array(
'x' => 0,
'y' => 0
);
}
// 解析して結果取得
static public function get()
{
// データチェック
self::check();
// 解析前
self::analyze();
// 解析後
self::pazzle();
}
// 現在のマップを解析
static private function analyze()
{
print("\033[2J");
self::$combo = array(); // コンボ初期化
for ($i = 0; $i < self::Y; $i++) {
for ($j = 0; $j < self::X; $j++) {
// コンボ成立判定
self::$tmp[$i][$j] = self::vcheck($i, $j) ? self::hcheck($i, $j) == self::H ? self::M : self::V : self::hcheck($i, $j);
// コンボ箇所に色をつける とりあえず
if (self::$tmp[$i][$j] != 0) {
self::$tmp[$i][$j] = self::$data[$i][$j];
}
// コンボデータを登録
if (self::$tmp[$i][$j] != 0) {
// 一度に消せる時の配列の持ち方など考える
//self::$comboCount++;
self::$combo[self::$data[$i][$j]][] = array(
$i,
$j
);
}
}
}
//self::combo();
//print("パズドラ\n");
self::dump(self::$tmp);
self::dump(self::$combo);
// var_dump(self::$combo);
self::dump(self::$data);
}
// 何のために用意したのか忘れた/(^o^)\
static private function combo()
{
$combo = 0;
// 色事に解析
foreach (self::$combo as $color => $lines) {
// 一コマずつ解析
foreach ($lines as $line) {
$combo += self::checkAAAA($line[0], $line[1]);
}
}
echo $combo;
}
// あるマスの周りに同じ色があるかチェック
static private function checkAAAA($_y, $_x)
{
$prevMove_y = 0;
$prevMove_x = 0;
$hit = 0;
$combo = 0;
// 周りを調査
foreach (self::$checkPattern as $key => $move) {
// 同じ色の周囲を検索
if (self::checkFinger($_y + $move[0], $_x + $move[1]) && self::$tmp[$_y][$_x] == self::$tmp[$_y + $move[0]][$_x + $move[1]] && $_y + $move[0] != $prevMove_y && $_x + $move[1] != $prevMove_x) {
//
self::$tmp[$_y][$_x] = 0;
$hit++;
$_y += $move[0];
$_x += $move[1];
$prevMove_y = $_y;
$prevMove_x = $_x;
if ($hit == 3)
$combo++;
continue;
}
echo "aa";
}
return $combo;
}
// 再帰的に最適回を探す
static function pazzle()
{
// 指の位置をセット
if (self::$finger['x'] == 0 && self::$finger['y'] == 0) {
self::setFinger(0, 0);
}
while (true) {
$move = fgets(STDIN, 10);
$move = rtrim($move, "\n");
if (!empty($move)) {
break;
}
}
// 動かしてみる
if ($move == 'k' || $move == '8')
self::moveUp();
if ($move == 'j' || $move == '2')
self::moveDown();
if ($move == 'l' || $move == '6')
self::moveRight();
if ($move == 'h' || $move == '4')
self::moveLeft();
if ($move == '9')
self::moveRightUp();
if ($move == '3')
self::moveRightDown();
if ($move == '7')
self::moveLeftUp();
if ($move == '1')
self::moveLeftDown();
if ($move == 'p')
self::autoPlay();
// 結果を出力
self::analyze();
self::combo();
self::pazzle();
}
// 今のところランダムで解いてる風
static private function autoPlay()
{
$cnt = 0;
while (true) {
usleep(50000);
self::moveMap();
if (self::$nextMove == 0) {
// 身動きが取れない
continue;
}
// 結果を出力
self::analyze();
self::combo();
$cnt++;
if ($cnt == self::MOVE_MAX) {
sleep(2);
break;
}
}
self::set();
self::autoPlay();
}
// 様々なアルゴリズムを使って次に動く場所を決める
// 一番のメイン部分!!!!
static private function moveMap()
{
$move = self::algorithm_01();
self::$nextMove = $move;
// 動かしてみる
switch (self::$nextMove) {
case 1:
self::moveUp();
break;
case 2:
self::moveDown();
break;
case 3:
self::moveRight();
break;
case 4:
self::moveLeft();
break;
case 5:
self::moveRightUp();
break;
case 6:
self::moveRightDown();
break;
case 7:
self::moveLeftUp();
break;
case 8:
self::moveLeftDown();
break;
}
}
// コンボ確定部分には移動しないアルゴリズム
// 説明:マップ上の動けるところにランダムで動いて
// コンボが確定した部分は通らない
static private function algorithm_01()
{
// コンボマップを取ってきて動ける場所を決める
$num = 0;
$ok = array();
$x = self::$finger['x'];
$y = self::$finger['y'];
// 指の動ける範囲を探す
foreach (self::$movePattern as $key => $move) {
$_y = $y + $move[0];
$_x = $x + $move[1];
if (self::checkFinger($_y, $_x) && !self::$tmp[$_y][$_x]) {
$ok[] = $key;
}
}
if (count($ok) > 1) {
shuffle($ok);
$num = mt_rand(0, count($ok) - 1);
}
return isset($ok[$num]) ? $ok[$num] : 0;
}
// マップ上で指から一番近いコンボを作っていく
// 基本アルゴリズム1を厳守
static private function algorithm_02()
{
}
// 創り上げたコンボを壊してでも最大最短コンボを導き出せる
static private function algorithm_03()
{
}
// ある色だけを重視してコンボを作る
static private function algorithm_04()
{
}
// ある色だけを重視してコンボを作る
static private function algorithm_05()
{
}
//
//
static private function play()
{
// コンボ順に消す
self::comboStart();
// 消えた部分を落下させる
// 落下した時に不足分は今回は付けない
self::comboFinish();
}
static private function comboStart()
{
}
static private function comboFinish()
{
}
// 有効な位置か判定
static private function validMap($i, $j)
{
return isset(self::$data[$i][$j]);
}
// 指の位置をチェック
// 必ず1つしか移動できない。スキップは出来ない
static private function checkFinger($i, $j)
{
// 横の範囲をチェック
if (self::$finger['x'] <= $j) {
$x = ($j - self::$finger['x']) <= 1 ? true : false;
} else {
$x = (self::$finger['x'] - $j) <= 1 ? true : false;
}
// 縦の範囲をチェック
if (self::$finger['y'] <= $i) {
$y = ($i - self::$finger['y']) <= 1 ? true : false;
} else {
$y = (self::$finger['y'] - $i) <= 1 ? true : false;
}
// 移動範囲のチェックとマップ内に存在するか
return ($x && $y && self::validMap($i, $j));
}
// 指の位置の初期設定(moveFingerのエイリアス)
static private function setFinger($i, $j)
{
self::moveFinger($i, $j);
}
// 指の位置を移動
static private function moveFinger($i, $j)
{
self::$finger['x'] = $j;
self::$finger['y'] = $i;
}
// 上に移動
static private function moveUp()
{
if (self::moveChange(1)) {
//echo $i.",".$j."から".($i-1).",".$j."上に移動\n";
}
}
// 下に移動
static private function moveDown()
{
if (self::moveChange(2)) {
}
}
// 右に移動
//
//
static private function moveRight()
{
if (self::moveChange(3)) {
}
}
// 左に移動
static private function moveLeft()
{
if (self::moveChange(4)) {
}
}
// 左上に移動
static private function moveLeftUp()
{
if (self::moveChange(7)) {
}
}
// 右上に移動
static private function moveRightUp()
{
if (self::moveChange(5)) {
}
}
// 左下に移動
static private function moveLeftDown()
{
if (self::moveChange(8)) {
}
}
// 右下に移動
static private function moveRightDown()
{
if (self::moveChange(6)) {
}
}
// 値を入れ替える
static private function moveChange($move)
{
// 現在値を取得
$i = self::$finger['y'];
$j = self::$finger['x'];
$_i = $i + self::$movePattern[$move][0];
$_j = $j + self::$movePattern[$move][1];
// 位置の変更が可能かチェック
if (!self::checkFinger($_i, $_j)) {
return false;
}
// 入れ替え
$tmp = self::$data[$i][$j];
self::$data[$i][$j] = self::$data[$_i][$_j];
self::$data[$_i][$_j] = $tmp;
// 現在値の更新
self::moveFinger($_i, $_j);
return true;
}
// 縦ラインのチェック
static private function vcheck($i, $j)
{
$check = false;
if ($i >= 0 && $i < self::Y - 2) {
$check = self::$data[$i][$j] === self::$data[$i + 1][$j] && self::$data[$i][$j] === self::$data[$i + 2][$j] ? true : false;
}
if ($i >= 1 && $i < self::Y - 1) {
if (!$check)
$check = self::$data[$i][$j] === self::$data[$i + 1][$j] && self::$data[$i][$j] === self::$data[$i - 1][$j] ? true : false;
}
if ($i >= 0 && $i >= 2) {
if (!$check)
$check = self::$data[$i][$j] === self::$data[$i - 1][$j] && self::$data[$i][$j] === self::$data[$i - 2][$j] ? true : false;
}
return $check;
}
// 横ラインのチェック
static private function hcheck($i, $j)
{
$check = false;
if ($j >= 0 && $j < self::X - 2) {
$check = self::$data[$i][$j] === self::$data[$i][$j + 1] && self::$data[$i][$j] === self::$data[$i][$j + 2] ? 2 : 0;
}
if ($j >= 1 && $j < self::X - 1) {
if (!$check)
$check = self::$data[$i][$j] === self::$data[$i][$j + 1] && self::$data[$i][$j] === self::$data[$i][$j - 1] ? 2 : 0;
}
if ($j >= 0 && $j >= 2) {
if (!$check)
$check = self::$data[$i][$j] === self::$data[$i][$j - 1] && self::$data[$i][$j] === self::$data[$i][$j - 2] ? 2 : 0;
}
return $check;
}
// 番号と色
static private function getColor($i = 0, $num = false)
{
switch ($i) {
case 1:
$code = self::COL_1;
break;
case 2:
$code = self::COL_2;
break;
case 3:
$code = self::COL_3;
break;
case 4:
$code = self::COL_4;
break;
case 5:
$code = self::COL_5;
break;
case 6:
$code = self::COL_6;
break;
default:
$code = self::COL_0;
}
return !$num ? sprintf(self::COL_BASE, $code) : $code;
}
// とりあえずダンプの糞コード
static private function dump($data)
{
if ($data === self::$data) {
for ($i = 0; $i < self::Y; $i++) {
for ($j = 0; $j < self::X; $j++) {
$mark = "●"; // $data[$i][$j]
if ($i == self::$finger['y'] && $j == self::$finger['x']) {
echo "\033[" . self::getColor($data[$i][$j], 1) . ";47;1m";
echo $mark . "\033[m";
} else {
echo self::getColor($data[$i][$j]);
echo $mark . self::getColor(0, 0);
}
}
echo "\n";
}
} elseif ($data === self::$tmp) {
echo "\033[0;31mPazzle\033[0;0m\n";
for ($i = 0; $i < self::Y; $i++) {
for ($j = 0; $j < self::X; $j++) {
$mark = "●"; // $data[$i][$j]
if ($i == self::$finger['y'] && $j == self::$finger['x']) {
echo "\033[37;47;1m";
echo $mark . "\033[m";
//echo self::getColor();
//echo $mark.self::getColor(0, 0);
} else {
echo self::getColor($data[$i][$j]);
echo $mark . self::getColor(0, 0);
}
}
echo "\n";
}
} else {
echo count($data), "コンボ\n";
//var_dump($data);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment