뱀 게임 만들기

어제 못 다한 뱀 게임을 만들었다.

 

1. List.Add() - 뱀 표시인 *가 하나만 찍히는 현상

문제의 코드

public class Snake
{
    List<Point> positions;

    public Snake(Point p, int l, Direction d)
    {
        positions = new List<Point>();
        Point tempPos = new Point(p.x, p.y, p.sym);

        for (int i = 0; i < l; ++i)
        {
            --tempPos.x;
            positions.Add(tempPos);
        }
    }
    ...
 }

 

 

고친 코드

public Snake(Point p, int l, Direction d)
{
	List<Point> positions;
    positions = new List<Point>();

    for (int i = 0; i < l; ++i)
    {
        Point tempPos = new Point(--p.x, p.y, p.sym);
        positions.Add(tempPos);
    }
}

 

List.Add는 값 복사(수정해도 원래의 값에 영향X)가 아니라 참조(영향 O)이다.

나는 값 복사라 생각하고 코드를 짰었다.

 

문제의 코드에서 --tempPos.x를 하면 원래의 값인 tempPos의 x도 전부 --되니

tempPos를 4번 Add한 리스트의 크기는 4이지만 다 같은 Position을 가진 일이 일어난 것이다.

 

 

비슷한 문제 발생

Point tempPoint = positions[0]; // 얕은 복사
Point tempPoint = new Point(positions[0].x, positions[0].y, positions[0].sym);
// 로 해결

 

 

2. 뱀 게임 완성

문제 없이 돌아간다!

 

뱀 게임 전체 코드

더보기
class SnakeGame
{
    static void Main(string[] args)
    {
        const int wallYSize = 20;
        const int wallXSize = 40;

        int eatenFoodNum = 0;

        // 뱀의 초기 위치와 방향을 설정하고, 그립니다.
        Point p = new Point(wallXSize / 2, wallYSize / 2, '*');
        Snake snake = new Snake(p, 4, Direction.RIGHT);
        snake.Draw();

        // 음식의 위치를 무작위로 생성하고, 그립니다.
        FoodCreator foodCreator = new FoodCreator(wallXSize, wallYSize, '$');
        Point food = foodCreator.CreateFood(snake.positions);
        food.Draw();

        DrawWall(wallXSize, wallYSize);

        // 게임 루프
        while (true)
        {
            // 키 입력이 있는 경우에만 방향을 변경
            if (Console.KeyAvailable)
            {
                ConsoleKey key = Console.ReadKey().Key;
                snake.ChangeDirection(key);
            }

            // 벽 충돌
            if (snake.isWallCollision(wallXSize, wallYSize))
            {
                Console.SetCursorPosition(0, wallYSize + 1);
                Console.WriteLine("벽과 충돌했습니다.");
                break;
            }

            // 몸통과 머리 충돌
            if (snake.isBodyCollision())
            {
                Console.SetCursorPosition(0, wallYSize + 1);
                Console.WriteLine("뱀의 머리와 몸통이 충돌했습니다.");
                break;
            }

            // 음식 먹기
            if (snake.positions[0].x == food.x && snake.positions[0].y == food.y)
            {
                ++eatenFoodNum;
                snake.Eat();

                foodCreator = new FoodCreator(wallXSize, wallYSize, '$');
                food = foodCreator.CreateFood(snake.positions);
                food.Draw();
            }

            snake.Move();
            snake.Draw();

            // 게임 속도 조절
            Thread.Sleep(100);

            // 뱀의 상태를 출력합니다 (예: 현재 길이, 먹은 음식의 수 등)
            Console.SetCursorPosition(0, wallYSize + 1);
            Console.WriteLine("현재 뱀의 길이: " + snake.positions.Count);
            Console.SetCursorPosition(0, wallYSize + 2);
            Console.WriteLine("먹은 음식의 수: " + eatenFoodNum);
        }

        Console.SetCursorPosition(0, wallYSize + 2);
        Console.WriteLine("최종 뱀의 길이: " + snake.positions.Count);
        Console.SetCursorPosition(0, wallYSize + 3);
        Console.WriteLine("최종 먹은 음식의 수: " + eatenFoodNum);
    }

    static void DrawWall(int xSize, int ySize)
    {
        for (int i = 0; i < xSize; i++)
        {
            Console.SetCursorPosition(i, 0);
            Console.Write('-');
            Console.SetCursorPosition(i, ySize);
            Console.Write('-');
        }
        for (int i = 0; i < ySize; i++)
        {
            Console.SetCursorPosition(0, i);
            Console.Write('|');
            Console.SetCursorPosition(xSize, i);
            Console.Write('|');
        }
    }
}

public class Snake
{
    Direction dir;
    bool isEat = false;

    public List<Point> positions { get; private set; }

    public Snake(Point p, int length, Direction d)
    {
        dir = d;

        positions = new List<Point>(10);
        for (int i = 0; i < length; ++i)
        {
            Point tempPos = new Point(--p.x, p.y, p.sym);
            positions.Add(tempPos);
        }
    }

    public void Draw()
    {
        for (int i = 0; i < positions.Count; ++i)
            positions[i].Draw();
    }

    public void Move()
    {
        Point tempPoint = new Point(positions[0].x, positions[0].y, positions[0].sym);
        switch (dir)
        {
            case Direction.UP:
                --tempPoint.y;
                break;
            case Direction.DOWN:
                ++tempPoint.y;
                break;
            case Direction.LEFT:
                --tempPoint.x;
                break;
            case Direction.RIGHT:
                ++tempPoint.x;
                break;
            default:
                break;
        }

        positions.Insert(0, tempPoint);

        if (!isEat)
        {
            positions[positions.Count - 1].Clear();
            positions.RemoveAt(positions.Count - 1);
        }
        else isEat = false;
    }

    public void Eat()
    {
        isEat = true;
    }

    public void ChangeDirection(ConsoleKey key)
    {
        if (key == ConsoleKey.UpArrow) dir = Direction.UP;
        else if (key == ConsoleKey.DownArrow) dir = Direction.DOWN;
        else if (key == ConsoleKey.LeftArrow) dir = Direction.LEFT;
        else if (key == ConsoleKey.RightArrow) dir = Direction.RIGHT;
    }

    public bool isWallCollision(int wallXsize, int wallYSize)
    {
        if (positions[0].x >= wallXsize || positions[0].x <= 0
                || positions[0].y >= wallYSize || positions[0].y <= 0)
            return true;
        else return false;
    }

    public bool isBodyCollision()
    {
        for (int i = 0; i < positions.Count; ++i)
            for (int j = 0; j < positions.Count; ++j)
                if (i != j
                    && positions[i].x == positions[j].x
                    && positions[i].y == positions[j].y)
                    return true;

        return false;
    }
}

public class FoodCreator
{
    int x;
    int y;
    char foodChar;
    Point p;

    public FoodCreator(int _x, int _y, char f)
    {
        x = _x;
        y = _y;
        foodChar = f;
        //p = new Point(x, y, f);
    }

    public Point CreateFood(List<Point> snakePositions)
    {
        bool isCollision = false;

        while (!isCollision)
        {
            Random random = new Random();
            x = random.Next(1, x);
            y = random.Next(1, y);

            p = new Point(x, y, foodChar);

            for (int i = 0; i < snakePositions.Count; ++i) {
                if (snakePositions[i].x == p.x && snakePositions[i].y == p.y)
                {
                    isCollision = false;
                    break;
                }
                else
                    isCollision = true;
            }
        }
        return p;
    }
}

public class Point
{
    public int x { get; set; }
    public int y { get; set; }
    public char sym { get; set; }

    // Point 클래스 생성자
    public Point(int _x, int _y, char _sym)
    {
        x = _x;
        y = _y;
        sym = _sym;
    }

    // 점을 그리는 메서드
    public void Draw()
    {
        Console.SetCursorPosition(x, y);
        Console.Write(sym);
    }

    // 점을 지우는 메서드
    public void Clear()
    {
        sym = ' ';
        Draw();
    }

    // 두 점이 같은지 비교하는 메서드
    public bool IsHit(Point p)
    {
        return p.x == x && p.y == y;
    }
}

// 방향을 표현하는 열거형입니다.
public enum Direction
{
    LEFT,
    RIGHT,
    UP,
    DOWN
}

 

 

블랙잭 만들기

 

텍스트로만 간단하게 구현해봤다.

 

블랙잭 코드

더보기

코드는 빠른 구현을 목적으로 짰기 때문에 마음에 들진 않는다.

public class Blackjack
{
    Player player;
    Dealer dealer;
    Deck deck;
    public Blackjack(Player _player, Dealer _dealer)
    {
        player = new Player();
        dealer = new Dealer();
        deck = new Deck();
    }

    // 딜러와 플레이어에게 2장의 카드 주기
    public void DealInitialCards()
    {
        Hit();
        Hit();
    }

    // 카드 더 받기
    public void Hit()
    {
        player.DrawCardFromDeck(deck);
        if (dealer.Hand.GetTotalValue() < 17)
            dealer.DrawCardFromDeck(deck);
    }

    // 플레이어가 stop했을 경우
    // 딜러의 카드가 17이 넘거나 17점이 될 때까지 딜러에게 카드 주기
    public void DealerHit()
    {
        while (dealer.Hand.GetTotalValue() != 17
            && dealer.Hand.GetTotalValue() < 17)
            dealer.DrawCardFromDeck(deck);
    }

    // 점수 비교, 승패 가르기
    public void WhoWin()
    {
        Console.Clear();

        WritePlayerInfo();
        WriteDealerInfo();

        int playerScore = (int)MathF.Abs(player.Hand.GetTotalValue() - 21);
        int dealerScore = (int)MathF.Abs(dealer.Hand.GetTotalValue() - 21);

        if(dealerScore > playerScore)
            Console.WriteLine("플레이어 승! ");
        else if(dealerScore == playerScore)
            Console.WriteLine("무승부!");
        else
            Console.WriteLine("딜러 승! ");

        Console.WriteLine("PlayerScore: " + player.Hand.GetTotalValue());
        Console.WriteLine("DealerScore: " + dealer.Hand.GetTotalValue());
    }

    public void WritePlayerInfo()
    {
        Console.WriteLine("Player Card Info");
        Console.WriteLine();

        for (int i = 0; i < player.Hand.cards.Count; ++i)
            Console.WriteLine( player.Hand.cards[i].ToString());

        Console.WriteLine("Player Total: " + player.Hand.GetTotalValue());
        Console.WriteLine();
        Console.WriteLine();
    }

    public void WriteDealerInfo()
    {
        Console.WriteLine("Dealer Card Info");
        Console.WriteLine();

        Console.WriteLine("딜러가 가지고 있는 카드의 개수: " + dealer.Hand.cards.Count + "개");
        Console.WriteLine(dealer.Hand.cards[0].ToString());

        Console.WriteLine();
        Console.WriteLine();
    }

    public bool isGameEnd()
    {
        if (player.Hand.GetTotalValue() >= 21)
            return true;

        return false;
    }
}

class Program
{
    static void Main(string[] args)
    {
        // 블랙잭 게임을 실행하세요
        while (true)
        {
            Player player = new Player();
            Dealer dealer = new Dealer();

            Blackjack blackjackGame = new Blackjack(player, dealer);
            blackjackGame.DealInitialCards();

            bool isGameOver = false;
            while (!isGameOver)
            {
                Console.Clear();

                blackjackGame.WritePlayerInfo();
                blackjackGame.WriteDealerInfo();

                Console.WriteLine("Hit : 1, Stand: 2");
                char c = Console.ReadLine()[0];

                if (c == '1')
                {
                    blackjackGame.Hit();
                    isGameOver = blackjackGame.isGameEnd();
                }
                else if (c == '2')
                {
                    blackjackGame.DealerHit();
                    isGameOver = true;
                }
                else continue;
            }
            
            blackjackGame.WhoWin();

            // 계속 게임을 할거냐
            Console.WriteLine("게임을 그만하려면 '0'을, 계속하려면 아무 문자나 입력해주세요.");
            if (Console.ReadLine()[0] == '0')
                break;
        }
    }
}