Codingame『HOURGlASS』


*1

本当に問題なのは

 「何言ってるのかよくわからん」が発生した。
あの説明で粒が減ったり落ちて溜まっていったりする仕組み理解できる?
ハァ?って言ってる猫ミームレベルで私には無理だった。
結局ここを理解するのにだいぶ時間食った。

もう一つの問題

 こっちは明確に説明が足りないと思うんだけど…。
時間経過後の砂時計の中の粒の形は、粒の初期位置に関係なく個数のみで決まるということ。
仮に与えられた初期状態では粒が飛び散っていても変な山ができたりしない。
粒が落ちる仕組みまで入れないといけないのかと思ってヒヤッとしたわ。

コード

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

/**
 * Auto-generated code below aims at helping you parse
 * the standard input according to the problem statement.
 **/
class Solution
{
    //フタと中央と壁
    static string HourglassCap = "+=====================+";
    static string HourglassCenter = "           X";
    static string WallA = @"\";
    static string WallB = @"/";

    //粒が消える(増える)順番
    static int[][] TopHalfGrainsNumber = new int[10][]{
        new int[19]{47,32,30,19,17,10,8,5,3,1,2,4,7,9,16,18,29,31,46},
        new int[17]{51,49,36,34,23,21,14,12,6,11,13,20,22,33,35,48,50},
        new int[15]{66,55,53,40,38,27,25,15,24,26,37,39,52,54,65},
        new int[13]{70,68,59,57,44,42,28,41,43,56,58,67,69},
        new int[11]{81,74,72,63,61,45,60,62,71,73,80},
        new int[9]{85,83,78,76,64,75,77,82,84},
        new int[7]{92,89,87,79,86,88,91},
        new int[5]{96,94,90,93,95},
        new int[3]{99,97,98},
        new int[1]{100},
    };
    static int[][] BottomHalfGrainsNumber = new int[10][]{
        new int[1]{1},
        new int[3]{100,2,99},
        new int[5]{98,82,3,81,97},
        new int[7]{96,80,66,4,65,79,95},
        new int[9]{94,78,64,52,5,51,63,77,93},
        new int[11]{92,76,62,50,40,6,39,49,61,75,91},
        new int[13]{90,74,60,48,38,30,7,29,37,47,59,73,89},
        new int[15]{88,72,58,46,36,28,22,8,21,27,35,45,57,71,87},
        new int[17]{86,70,56,44,34,26,20,16,9,15,19,25,33,43,55,69,85},
        new int[19]{84,68,54,42,32,24,18,14,12,10,11,13,17,23,31,41,53,67,83}
    };

    //経過時間
    static int ElapsedTime;

    //粒の数
    static int TopHalfGrainCount;
    static int BottomHalfGrainCount;

    //砂時計の最終状態
    static StringBuilder Hourglass = new StringBuilder();
   
    static void Main(string[] args)
    {
        //粒を数える
        CountGrain();

        //100粒じゃない
        if (!IsGrainCountEqual100())
        {
            Console.WriteLine("BROKEN HOURGLASS");
            return;
        }

        //経過時間の取得
        ReadElapsedTime();

        //時間経過後の砂時計
        GrainCountAfterElapsedTime();

        //描画
        Draw();
    }

    //粒を数える
    static void CountGrain()
    {
        TopHalfGrainCount = CountGrainInHalf();
        var line = Console.ReadLine();
        Console.Error.WriteLine(line);
        BottomHalfGrainCount = CountGrainInHalf();

        Console.Error.WriteLine($"topcount:{TopHalfGrainCount}");
        Console.Error.WriteLine($"bottomcount:{BottomHalfGrainCount}");
    }

    //上下片側の粒を数える
    static int CountGrainInHalf()
    {
        var count = 0;

        for (int i = 0; i < 11; i++)
        {
            var line = Console.ReadLine();
            count += line.Count(x => x == 'o');
            Console.Error.WriteLine(line);
        }

        return count;
    }

    //経過時間を取得
    static void ReadElapsedTime()
    {
        ElapsedTime = int.Parse(Console.ReadLine());
        Console.Error.WriteLine($"ElapsedTime:{ElapsedTime}");
    }

    //100粒か否か
    static bool IsGrainCountEqual100()
    {
        return TopHalfGrainCount + BottomHalfGrainCount == 100;
    }

    //経過時間後の上下の粒の数
    static void GrainCountAfterElapsedTime()
    {
        TopHalfGrainCount = TopHalfGrainCountAfterElapsedTime();
        BottomHalfGrainCount = BottomHalfGrainCountAfterElapsedTime();
        Console.Error.WriteLine($"AfterTopGrain:{TopHalfGrainCount}");
        Console.Error.WriteLine($"AfterBottomGrain:{BottomHalfGrainCount}");
    }

    //時間経過後の上半分の物の数
    static int TopHalfGrainCountAfterElapsedTime()
    {
        var count = TopHalfGrainCount - ElapsedTime;
        return count < 0 ? 0 : count;
    }

    //経過時間後の下半分の粒の数
    static int BottomHalfGrainCountAfterElapsedTime()
    {
        var count = BottomHalfGrainCount + ElapsedTime;
        return count > 100 ? 100 : count;
    }

    //砂時計の描画
    static void Draw()
    {
        MakeTopHalf();
        Hourglass.AppendLine(HourglassCenter);
        MakeBottomHalf();

        Console.Write(Hourglass.ToString());
    }

    //砂時計上側の作成
    private static void MakeTopHalf()
    {
        DeleteTopHalfGrains();

        Hourglass.AppendLine(HourglassCap);

        for(var h = 0; h < 10; h++)
        {
            //スペース
            for(var s = 0; s <= h; s++)
                Hourglass.Append(" ");
            
            //左壁
            Hourglass.Append(WallA);

            //粒
            for(var x = 0; x < TopHalfGrainsNumber[h].Length; x++)
                if(TopHalfGrainsNumber[h][x] != 0) Hourglass.Append("o");
                else Hourglass.Append(" ");
            
            //右壁
            Hourglass.AppendLine(WallB);
        }
    }

    //上半分の不要な粒を消す
    private static void DeleteTopHalfGrains()
    {
        var grains = 101 - TopHalfGrainCount;

        for(var h = 0; h < TopHalfGrainsNumber.Length; h++)
        {
            for(var i = 0; i < TopHalfGrainsNumber[h].Length; i++)
                if(TopHalfGrainsNumber[h][i] < grains) TopHalfGrainsNumber[h][i] = 0;
        }
    }

    //砂時計下側の作成
    private static void MakeBottomHalf()
    {
        DeleteBottomHalfGrains();

        for(var h = 0; h < 10; h++)
        {
            //スペースの追加
            for(var s = 0; s <= 9-h; s++)
                Hourglass.Append(" ");
            
            //左壁
            Hourglass.Append(WallB);

            //粒
            for(var x = 0; x < BottomHalfGrainsNumber[h].Length; x++)
                if(BottomHalfGrainsNumber[h][x] != 0) Hourglass.Append("o");
                else Hourglass.Append(" ");
            
            //右壁
            Hourglass.AppendLine(WallA);
        }

        Hourglass.AppendLine(HourglassCap);
    }

    //下半分の不要な粒を消す
    private static void DeleteBottomHalfGrains()
    {
        for(var h = 0; h < BottomHalfGrainsNumber.Length; h++)
        {
            for(var i = 0; i < BottomHalfGrainsNumber[h].Length; i++)
                if(BottomHalfGrainsNumber[h][i] > BottomHalfGrainCount) BottomHalfGrainsNumber[h][i] = 0;
        }
    }
}


 もうちょっと色々省略できた気がする。
でも全テスト通ったからまぁいいでしょう。

*1:Copilot GPT Designer で作成