【C#】【数独】ファイルからデータを取り込む

なんか、とあるプログラミングスクールの入学試験でこういう問題は出題されたらしい。

合格できると、スクールの費用が全額無料ということなのですが、やはり、その門のハードルは高いようです。

で、オイラも腕試しで数独(ナンバープレイス)を解くプログラミングをやってみようと思いました。

まずは、問題を読み取るところから。(そこからかよ!)

とにかく、今回はUIは気にしないで、

こんな感じのファイルを取り込んで、デバッグ機能としてそのまま出力するところまでやります。

コードはこんな感じになりました。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace sudoku
{
    class Program
    {
        static void Main(string[] args)
        {
            int[,] matrix = new int[9, 9];

            // パラメータチェック
            if (args.Length != 1)
            {
                Console.WriteLine("usage : sudoku.exe [input file]");
                return;
            }

            // ファイルの存在を確認
            string filePath = Environment.CurrentDirectory + "\\" + args[0];
            if (File.Exists(filePath) == false)
            {
                Console.WriteLine("File not found.");
                return;
            }

            // ファイルを開く
            bool error = false;
            using (var stream = new StreamReader(filePath))
            {
                int row = 0;
                while(stream.EndOfStream == false)
                {
                    string lineText = stream.ReadLine();
                    var val = lineText.Split(',');
                    int col = 0;
                    foreach(var v in val)
                    {
                        int i;
                        if(int.TryParse(v, out i))
                        {
                            matrix[row, col] = i;
                        }
                        else
                        {
                            error = true;
                        }
                        col++;
                    }
                    row++;
                    if(row > 9)
                    {
                        error = true;
                    }
                }
            }
            if (error)
            {
                Console.WriteLine("Illegal format.");
                return;
            }

            // debug
            using (var stream = new StreamWriter(System.Environment.CurrentDirectory + "\\output"))
            {
                for(int row = 0; row < 9; row++)
                {
                    for(int col = 0; col < 9; col++)
                    {
                        stream.Write(matrix[row, col]);
                    }
                    stream.Write("\r\n");
                }
            }
        }
    }
}

実行結果

引数でファイル名を取得する

Main()の引数args[]の中にコマンドパラメータが入っています。

今回使用する引数は、ファイル名1個だけですので、args.lengthが1以外の場合は、usageを表示して終了します。

ファイルの存在を確認する

今回はカレントフォルダに存在するファイルを対象にすることにします。

おそらく相対パスなら大丈夫かもしれませんが、絶対パスならエラーになるでしょう。

対策は後で考えます。力を入れるべきところはここじゃないので。

Environment.CurrentDirectoryにカレントフォルダが入っているので、これにファイル名をくっつけて、完全なファイルパスを作成します。

そして、File.Exists()でファイルの存在を確認します。存在しなければfalseが返るので、エラーメッセージを表示して終了します。

ファイルのデータを取り込む

テキストのデータ読み取りならStreamReaderを使うのが簡単でしょう。

StreamReaderでファイルを開いて、ファイルストリームを取得します。

usingを使うと、usingの中でExceptionが発生しても、適切なtry/catch処理をやってくれます。

まぁ、エラーが起こってもプログラムがファイルを掴んだままにならない、ということです。

usingの中でwhileループをおこない、ReadLine()でファイルから1行ずつ取り出します。

取り出した1行データをsplit()を使って、”,”(カンマ)で分割します。結果はstring[]になります。

これをさらにtryParse()でint型に変換します。

これで読み込んだ文字列が数字に変換されて取り込むことができます。

これを全てのデータに対して行います。

念のためフォーマットエラーも確認します。

まぁ、クラス構成はもうちょっと考えるとして、とりあえずはこんな感じで。

【Laravel】【ホテル予約管理】ユーザーアカウントで予約登録

前回までの状況はこちら

最新ソースはこちら(gitHub)

https://github.com/takishita2nd/hotel-mng

今回はユーザーアカウントでログインした状態で予約登録する場合を作成していきます。

予約登録画面。

今までは宿泊者の情報を入力していますが、今回の仕様変更で、現在ログインしているユーザーの情報を表示させます。

                <table class="edit">
                    <tr>
                        <th>名前</th>
                        <td>{!! $user->name !!}</td>
                    </tr>
                    <tr>
                        <th>住所</th>
                        <td>{!! $user->address !!}</td>
                    </tr>
                    <tr>
                        <th>電話番号</th>
                        <td>{!! $user->phone !!}</td>
                    </tr>
                    <tr>
                        <th>人数</th>
                        <td>{!! Form::select('num', ['1' => 1, '2' => 2]) !!}</td>
                    </tr>
                    <tr>
                        <th>宿泊部屋</th>
                        <td>{!! Form::select('room', $rooms) !!}</td>
                    </tr>
                    <tr>
                        <th>宿泊日数</th>
                        <td>{!! Form::number('days', 1) !!}</td>
                    </tr>
                    <tr>
                        <th>宿泊日</th>
                        <td>{!! Form::date('start_day', \Carbon\Carbon::now()) !!}</td>
                    </tr>
                    <tr>
                        <th>チェックアウト</th>
                        <td>{!! Form::select('checkout', $timelist) !!}</td>
                    </tr>
                </table>
    /**
     * 入力フォーム
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        return view('register.create',
                    [
                        'user' => Auth::user(),
                        'rooms' => $this->roomRepository->getRoomList(),
                        'timelist' => $this->registerManagement->getTimeList()
                    ]
                );
    }

ログイン中のユーザー情報はAuth::user()で簡単に取り出すことができます。

予約登録処理。

まずは、バリデーションルールを修正します。

    public function rules()
    {
        return [
            'num' => 'required|numeric|digits_between:1,2',
            'room' => 'required|numeric',
            'days' => 'required|numeric|digits_between:1,4',
            'start_day' => 'required|date',
        ];
    }

登録するデータを変更します。

class RemoveCalumnReserveManagementTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('reserve_managements', function (Blueprint $table) {
            $table->dropColumn('name');
            $table->dropColumn('address');
            $table->dropColumn('phone');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('reserve_managements', function (Blueprint $table) {
            $table->string('name')->befor('num');
            $table->string('address')->after('name');
            $table->string('phone')->after('address');
        });
    }
}
class AddReserveManagementUserTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('reserve_management_user', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('reserve_management_id')
                  ->foreign('reserve_management_id')
                  ->references('id')->on('reserve_managements')
                  ->onDelete('cascade');
            $table->integer('user_id')
                  ->foreign('user_id')
                  ->references('id')->on('users')
                  ->onDelete('cascade');
            $table->timestamps();
            $table->engine = 'InnoDB';
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('reserve_management_user');
    }
}
    private $paramNames = ['num', 'days', 'start_day', 'lodging', 'checkout'];

予約データとユーザーを結びつけます。

データベースの構成はこんな感じになります。

    public function add($param, $room, $user)
    {
        $model = new ReserveManagement;
        foreach($this->paramNames as $name)
        {
            $model->$name = $param[$name];
        }
        $model->save();
        $this->attachToRoom($model, $room);
        $this->attachToSchedule($model);
        $this->attachToUser($model, $user);
    }
    public function attachToUser($model, $user)
    {
        $model->users()->attach($user);
    }

    public function detachToUser($model, $user)
    {
        $model->users()->detach($user);
    }
    public function store(ManagementRequest $request)
    {
        if($this->registerManagement->checkSchedule($request->start_day, 
                                                    $request->days, 
                                                    $request->room) == false)
        {
            return redirect('management/create')
                        ->with(['error' => 'スケジュールが重複します'])
                        ->withInput();
        }
        $param = $this->registerManagement->getParam();
        $this->registerManagement->add([
            $param[0] => $request->num,
            $param[1] => $request->days,
            $param[2] => $request->start_day,
            $param[3] => false,
            $param[4] => date('Y-m-d H:i', strtotime($request->start_day.'+'.$request->days.' day') + $request->checkout)
        ], $request->room, Auth::user());
        return redirect('management');
    }

最後に、登録した情報を予約一覧に表示させます。

ユーザーでログインしているときは、他のユーザーの情報が見えないようにしないといけません。

    public function getList()
    {
        $select = ['reserve_managements.id as id', 'num', 'rooms.id as roomid', 'rooms.name as room', 'days', 'checkout', 'start_day'];
        return ReserveManagement::select($select)
                                    ->where('lodging', false)
                                    ->orderBy('start_day')
                                    ->leftJoin('reserve_management_room', 'reserve_managements.id', '=', 'reserve_management_room.reserve_management_id')
                                    ->leftJoin('rooms', 'reserve_management_room.room_id', '=', 'rooms.id')
                                    ->get();
    }

    public function getListByMonth($year, $month, $room, $userId)
    {
        $select = ['reserve_managements.id as id', 'users.name as name', 'users.address as address', 'users.phone as phone', 'num', 'rooms.id as roomid', 'rooms.name as room', 'days', 'checkout', 'start_day'];
        return ReserveManagement::select($select)
                                ->leftJoin('reserve_management_room', 'reserve_managements.id', '=', 'reserve_management_room.reserve_management_id')
                                ->leftJoin('rooms', 'reserve_management_room.room_id', '=', 'rooms.id')
                                ->leftJoin('reserve_management_user', 'reserve_managements.id', '=', 'reserve_management_user.reserve_management_id')
                                ->leftJoin('users', 'reserve_management_user.user_id', '=', 'users.id')
                                ->where('start_day', '>=', date('Y-m-d', strtotime('first day of '.$year.'-'.$month)))
                                ->where('start_day', '<=', date('Y-m-d', strtotime('last day of '.$year.'-'.$month)))
                                ->where('reserve_management_room.room_id', $room)
                                ->where('lodging', false)
                                ->where('users.id', $userId)
                                ->orderBy('start_day')
                                ->get();
    }
    public function registers(Request $request)
    {
        return response()->json(['registerLists' => $this->registerManagement->getListByMonth(
            $request->year,
            $request->month,
            $request->room,
            Auth::user()->id
        )]);
    }

まだ十分ではないけれど、そこそこ形になってきました。