PHP의 GD 라이브러리를 사용하여 "많은 이미지에서 미묘하게 색다른 녀석 맞추는 게임"을 조금이라도 치트되지 않도록 만들고 싶습니다.

소스 코드는 여기

"많은 이미지 중 미묘하게 다른 녀석 맞추는 게임 (이름 긴)"이 자주 있다고 생각합니다.
이것을 프런트의 세계(HTML/CSS, JavaScript)만으로 구현하면, 눈으로 대답을 모르더라도 치트되어 정답할 수 있을 우려가 있습니다.
그것을 조금이라도 치트하기 어렵게 하자는 것이 이번 기획입니다.



이번에 실시하는 대책



개발자 도구에서 색상 정보를 모르는 것을 방지



CSS에서 배경색을 지정하면 개발자 도구에서 볼 수 있습니다.
그러므로 백엔드에서 이미지를 동적으로 생성한 후 프론트에서는 이미지를 로드하기만 하면 됩니다.

그 외, 「정오 판정을 프런트 엔드로 실장하지 않는다」등의 대책도 필요합니다만, 기사에서는 생략합니다.

참고 페이지



PHP로 이미지를 동적으로 생성하는 【GD편】 | 바샤로그.

PHP 사용



이번에는 PHP의 GD 확장 모듈을 사용하여 이미지를 생성합니다.
GD 확장 모듈은 오래된 PHP가 아니면 포함되어 있어야 합니다.php.ini 를 열고 다음과 같은 행이 있으면 첫 번째 세미콜론을 지우고 활성화합니다.
;extension=gd2

귀하의 PHP에서 phpinfo()를 실행하여 GD에 대한 정보를 볼 수 있는지 확인하십시오.
다음과 같이 보이면 성공입니다.



실제로 만들어 보자



GD 라이브러리를 사용하여 100×100의 단색 이미지(색은 무작위)를 만들고 출력합니다.


$width = 100;
$height = 100;

// 画像リソースを作成する
$img = imagecreate($width, $height);

// 色を付ける
imagecolorallocate($img, rand(0, 255), rand(0, 255), rand(0, 255));

// 出力する
$filename = 'images/image.png';
imagepng($img, $filename);
imagecreate 함수로 새 이미지를 만들고 imagecolorallocate 함수로 채색, imagepng 함수로 파일로 출력합니다.

3장의 단색 화상(1장만 색이 다른)을 출력해 정답 데이터를 유지한다



이미지를 1장 출력하는 패턴을 위에서 했으므로, 다음은 복수매(이번은 3장) 출력해, 정답을 나타내는 것도 출력합니다.
착색 패턴은 정답·부정해의 2가지 준비하면 OK로, 정답 데이터는 텍스트 파일에 숫자만 써 둡니다.

메인 로직입니다.

create_images.php
<?php

require_once __DIR__ . '/Image/ImageResourceFactory.php';
require_once __DIR__ . '/Image/Image.php';

use Image\Image;

function main($imageCount)
{
    $correctColor = array(
        'red' => getRandomInt(0, 255),
        'green' => getRandomInt(0, 255),
        'blue' => getRandomInt(0, 255),
    );
    $incorrectColor = array(
        'red' => getRandomInt(0, 255),
        'green' => getRandomInt(0, 255),
        'blue' => getRandomInt(0, 255),
    );

    $collectImageIndex = getRandomInt(0, $imageCount - 1);

    $width = 100;
    $height = 100;

    for ($i = 0; $i < $imageCount; ++$i) {
        $filename = __DIR__ . '/../data/image' . $i . '.png';
        if ($i === $collectImageIndex) {
            outputImage($width, $height, $correctColor, $filename);
        } else {
            outputImage($width, $height, $incorrectColor, $filename);
        }
    }

    file_put_contents(__DIR__ . '/../data/answer.txt', $collectImageIndex);
}

function getRandomInt(int $min, int $max = null, int $fixed = null): int
{
    if (isset($fixed)) {
        return $fixed;
    }
    return rand($min, $max);
}

function outputImage(int $width, int $height, array $color, string $filename)
{
    try {
        $image = new Image($width, $height);
        $image->paintIn($color['red'], $color['green'], $color['blue']);
        $image->saveAs($filename);
    } catch (Exception $e) {
    }
}

메인 로직을 실행하는데 있어서 클래스로서 잘라낸, 이미지의 처리류입니다.

Image/Image.php
<?php

namespace Image;

use Exception;
use Image\ImageResourceFactory;

/**
 * Class Image
 * 画像の作成・編集・保存
 */
class Image
{
    /**
     * @var resource $resource - 画像リソース
     */
    public $resource;

    /**
     * Image constructor.
     * @param int $width - 画像の幅
     * @param int $height - 画像の高さ
     * @throws Exception - 画像作成に失敗した場合、例外を返します。
     */
    public function __construct(int $width, int $height)
    {
        $this->resource = ImageResourceFactory::getImageResource($width, $height);
    }

    public function paintIn(int $red, int $green, int $blue)
    {
        imagecolorallocate($this->resource, $red, $green, $blue);
    }

    public function saveAs(string $filename)
    {
        imagepng($this->resource, $filename);
    }
}

Image/ImageResourceFactory.php
<?php

namespace Image;

use Exception;

/**
 * Class ImageResourceFactory
 * 画像リソース作成用
 */
class ImageResourceFactory
{
    /**
     * 画像のリソースを取得します。
     *
     * @param int $width - 画像の幅
     * @param int $height - 画像の高さ
     * @return resource - 画像リソース
     * @throws Exception - 画像作成に失敗した場合、例外を返します。
     */
    public static function getImageResource(int $width, int $height)
    {
        $resource = imagecreate($width, $height);
        if ($resource === false) {
            throw new Exception('Failed to create image resource.');
        }
        return $resource;
    }
}

정오 판정에 대해서도, 프런트의 JavaScript가 아니라 PHP로 구현해, 화면에 링크를 붙입니다(여기에서는 생략 합니다).

감상



이런 종류의 게임은 전부터 만들려고 했지만, 광고계의 업무를 하고 있었기 때문에 base64 형식을 사용한 방법을 찾고 있었다.
base64 에서는 1 x 1 의 흑색·투명 화상의 만드는 방법 밖에 발견되지 않고, 여러가지 엄청나게 살고 있었습니다만, 이번 PHP 속에 화상을 만들 수 있는 라이브러리가 동봉되고 있었다는 것을 깨달은 것은 크다 입니다.

솔직히, 온라인 대전이나 인터넷 랭킹 등을 포함하지 않는 게임이라면, 치트할지 어떨지는 최종 유저의 자유이며, 일부러 백엔드에 처리를 시킬 필요도 없을 것입니다.
하지만, 「프런트는 모두 보이고 있어, 얼마든지 개조할 수 있다」라고 하는 관점에서, 치트되고 싶지 않은 부분을 백엔드에 의해 실장할 수 있었던 것은, 향후의 업무에도 살릴 것 같은 부분이라고 생각합니다.

이번 기사가 여러분의 무언가의 계기가 되시면 다행입니다.

좋은 웹페이지 즐겨찾기