{"id":21509939,"url":"https://github.com/meowv/2048","last_synced_at":"2025-07-01T12:35:58.164Z","repository":{"id":115854610,"uuid":"74534820","full_name":"Meowv/2048","owner":"Meowv","description":"2048 ConsoleApplication、C#","archived":false,"fork":false,"pushed_at":"2016-11-23T03:05:52.000Z","size":9,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-24T01:11:13.483Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Meowv.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-11-23T02:56:12.000Z","updated_at":"2016-11-23T03:06:32.000Z","dependencies_parsed_at":null,"dependency_job_id":"ccf46100-4376-44ba-a7f2-a3bcacd025db","html_url":"https://github.com/Meowv/2048","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Meowv%2F2048","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Meowv%2F2048/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Meowv%2F2048/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Meowv%2F2048/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Meowv","download_url":"https://codeload.github.com/Meowv/2048/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244056430,"owners_count":20390720,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-23T21:34:53.480Z","updated_at":"2025-03-17T15:16:24.689Z","avatar_url":"https://github.com/Meowv.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 2048\n\n###2048.cs\n\n```\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace _2048\n{\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            Game _2048 = new Game();\n            _2048.Run();\n        }\n    }\n}\n\n```\n\n###2048.cs\n\n```\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace _2048\n{\n    public class Game\n    {\n        public ulong Score { get; private set; }\n        public ulong[,] Board { get; private set; }\n\n        private readonly int nRows;\n        private readonly int nCols;\n        private readonly Random random = new Random();\n\n        public Game()\n        {\n            this.Board = new ulong[4, 4];\n            this.nRows = this.Board.GetLength(0);\n            this.nCols = this.Board.GetLength(1);\n            this.Score = 0;\n        }\n\n        public void Run()\n        {\n            bool hasUpdated = true;\n            do\n            {\n                if (hasUpdated)\n                {\n                    PutNewValue();\n                }\n\n                Display();\n\n                if (IsDead())\n                {\n                    using (new ColorOutput(ConsoleColor.Red))\n                    {\n                        Console.WriteLine(\"YOU ARE DEAD!!!\");\n                        break;\n                    }\n                }\n\n                Console.WriteLine(\"Use arrow keys to move the tiles. Press Ctrl-C to exit.\");\n                ConsoleKeyInfo input = Console.ReadKey(true); // BLOCKING TO WAIT FOR INPUT\n                Console.WriteLine(input.Key.ToString());\n\n                switch (input.Key)\n                {\n                    case ConsoleKey.UpArrow:\n                        hasUpdated = Update(Direction.Up);\n                        break;\n\n                    case ConsoleKey.DownArrow:\n                        hasUpdated = Update(Direction.Down);\n                        break;\n\n                    case ConsoleKey.LeftArrow:\n                        hasUpdated = Update(Direction.Left);\n                        break;\n\n                    case ConsoleKey.RightArrow:\n                        hasUpdated = Update(Direction.Right);\n                        break;\n\n                    default:\n                        hasUpdated = false;\n                        break;\n                }\n            }\n            while (true); // use CTRL-C to break out of loop\n\n            Console.WriteLine(\"Press any key to quit...\");\n            Console.Read();\n        }\n\n        private static ConsoleColor GetNumberColor(ulong num)\n        {\n            switch (num)\n            {\n                case 0:\n                    return ConsoleColor.DarkGray;\n                case 2:\n                    return ConsoleColor.Cyan;\n                case 4:\n                    return ConsoleColor.Magenta;\n                case 8:\n                    return ConsoleColor.Red;\n                case 16:\n                    return ConsoleColor.Green;\n                case 32:\n                    return ConsoleColor.Yellow;\n                case 64:\n                    return ConsoleColor.Yellow;\n                case 128:\n                    return ConsoleColor.DarkCyan;\n                case 256:\n                    return ConsoleColor.Cyan;\n                case 512:\n                    return ConsoleColor.DarkMagenta;\n                case 1024:\n                    return ConsoleColor.Magenta;\n                default:\n                    return ConsoleColor.Red;\n            }\n        }\n\n        private static bool Update(ulong[,] board, Direction direction, out ulong score)\n        {\n            int nRows = board.GetLength(0);\n            int nCols = board.GetLength(1);\n\n            score = 0;\n            bool hasUpdated = false;\n\n            // You shouldn't be dead at this point. We always check if you're dead at the end of the Update()\n\n            // Drop along row or column? true: process inner along row; false: process inner along column\n            bool isAlongRow = direction == Direction.Left || direction == Direction.Right;\n\n            // Should we process inner dimension in increasing index order?\n            bool isIncreasing = direction == Direction.Left || direction == Direction.Up;\n\n            int outterCount = isAlongRow ? nRows : nCols;\n            int innerCount = isAlongRow ? nCols : nRows;\n            int innerStart = isIncreasing ? 0 : innerCount - 1;\n            int innerEnd = isIncreasing ? innerCount - 1 : 0;\n\n            Func\u003cint, int\u003e drop = isIncreasing\n                ? new Func\u003cint, int\u003e(innerIndex =\u003e innerIndex - 1)\n                : new Func\u003cint, int\u003e(innerIndex =\u003e innerIndex + 1);\n\n            Func\u003cint, int\u003e reverseDrop = isIncreasing\n                ? new Func\u003cint, int\u003e(innerIndex =\u003e innerIndex + 1)\n                : new Func\u003cint, int\u003e(innerIndex =\u003e innerIndex - 1);\n\n            Func\u003culong[,], int, int, ulong\u003e getValue = isAlongRow\n                ? new Func\u003culong[,], int, int, ulong\u003e((x, i, j) =\u003e x[i, j])\n                : new Func\u003culong[,], int, int, ulong\u003e((x, i, j) =\u003e x[j, i]);\n\n            Action\u003culong[,], int, int, ulong\u003e setValue = isAlongRow\n                ? new Action\u003culong[,], int, int, ulong\u003e((x, i, j, v) =\u003e x[i, j] = v)\n                : new Action\u003culong[,], int, int, ulong\u003e((x, i, j, v) =\u003e x[j, i] = v);\n\n            Func\u003cint, bool\u003e innerCondition = index =\u003e Math.Min(innerStart, innerEnd) \u003c= index \u0026\u0026 index \u003c= Math.Max(innerStart, innerEnd);\n\n            for (int i = 0; i \u003c outterCount; i++)\n            {\n                for (int j = innerStart; innerCondition(j); j = reverseDrop(j))\n                {\n                    if (getValue(board, i, j) == 0)\n                    {\n                        continue;\n                    }\n\n                    int newJ = j;\n                    do\n                    {\n                        newJ = drop(newJ);\n                    }\n                    // Continue probing along as long as we haven't hit the boundary and the new position isn't occupied\n                    while (innerCondition(newJ) \u0026\u0026 getValue(board, i, newJ) == 0);\n\n                    if (innerCondition(newJ) \u0026\u0026 getValue(board, i, newJ) == getValue(board, i, j))\n                    {\n                        // We did not hit the canvas boundary (we hit a node) AND no previous merge occurred AND the nodes' values are the same\n                        // Let's merge\n                        ulong newValue = getValue(board, i, newJ) * 2;\n                        setValue(board, i, newJ, newValue);\n                        setValue(board, i, j, 0);\n\n                        hasUpdated = true;\n                        score += newValue;\n                    }\n                    else\n                    {\n                        // Reached the boundary OR...\n                        // we hit a node with different value OR...\n                        // we hit a node with same value BUT a prevous merge had occurred\n                        // \n                        // Simply stack along\n                        newJ = reverseDrop(newJ); // reverse back to its valid position\n                        if (newJ != j)\n                        {\n                            // there's an update\n                            hasUpdated = true;\n                        }\n\n                        ulong value = getValue(board, i, j);\n                        setValue(board, i, j, 0);\n                        setValue(board, i, newJ, value);\n                    }\n                }\n            }\n\n            return hasUpdated;\n        }\n\n        private bool Update(Direction dir)\n        {\n            ulong score;\n            bool isUpdated = Game.Update(this.Board, dir, out score);\n            this.Score += score;\n            return isUpdated;\n        }\n\n        private bool IsDead()\n        {\n            ulong score;\n            foreach (Direction dir in new Direction[] { Direction.Down, Direction.Up, Direction.Left, Direction.Right })\n            {\n                ulong[,] clone = (ulong[,])Board.Clone();\n                if (Game.Update(clone, dir, out score))\n                {\n                    return false;\n                }\n            }\n\n            // tried all directions. none worked.\n            return true;\n        }\n\n        private void Display()\n        {\n            Console.Clear();\n            Console.WriteLine();\n            for (int i = 0; i \u003c nRows; i++)\n            {\n                for (int j = 0; j \u003c nCols; j++)\n                {\n                    using (new ColorOutput(Game.GetNumberColor(Board[i, j])))\n                    {\n                        Console.Write(string.Format(\"{0,6}\", Board[i, j]));\n                    }\n                }\n\n                Console.WriteLine();\n                Console.WriteLine();\n            }\n\n            Console.WriteLine(\"Score: {0}\", this.Score);\n            Console.WriteLine();\n        }\n\n        private void PutNewValue()\n        {\n            // Find all empty slots\n            List\u003cTuple\u003cint, int\u003e\u003e emptySlots = new List\u003cTuple\u003cint, int\u003e\u003e();\n            for (int iRow = 0; iRow \u003c nRows; iRow++)\n            {\n                for (int iCol = 0; iCol \u003c nCols; iCol++)\n                {\n                    if (Board[iRow, iCol] == 0)\n                    {\n                        emptySlots.Add(new Tuple\u003cint, int\u003e(iRow, iCol));\n                    }\n                }\n            }\n\n            // We should have at least 1 empty slot. Since we know the user is not dead\n            int iSlot = random.Next(0, emptySlots.Count); // randomly pick an empty slot\n            ulong value = random.Next(0, 100) \u003c 95 ? (ulong)2 : (ulong)4; // randomly pick 2 (with 95% chance) or 4 (rest of the chance)\n            Board[emptySlots[iSlot].Item1, emptySlots[iSlot].Item2] = value;\n        }\n\n        #region Utility Classes\n        enum Direction\n        {\n            Up,\n            Down,\n            Right,\n            Left,\n        }\n\n        class ColorOutput : IDisposable\n        {\n            public ColorOutput(ConsoleColor fg, ConsoleColor bg = ConsoleColor.Black)\n            {\n                Console.ForegroundColor = fg;\n                Console.BackgroundColor = bg;\n            }\n\n            public void Dispose()\n            {\n                Console.ResetColor();\n            }\n        }\n        #endregion Utility Classes\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmeowv%2F2048","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmeowv%2F2048","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmeowv%2F2048/lists"}