과제
Milk라는 클래스와 VendingMachine 이라는 클래스를 하나 만들겠습니다. Milk의 맴버변수로, 유통기한을 나타내는 int를 작성해주시기 바랍니다.
VendingMachine 클래스에는 Queue를 활용하여 Milk를 담을 수 있는 컨테이너를 필드로 작성하여 주시기 바랍니다.
벤딩머신의 메소드로, 우유를 집어넣는 코드와 우유를 꺼내는 기능을 작성하되, 꺼낼때는 콘솔에 유통기한 및 큐에 남아있는 갯수를 출력하는 기능을 작성해주시기 바랍니다. 갯수가 0일때 우유를 꺼내는 기능을 호출하게 되면 꺼내는 대신 다른 멘트가 나오게끔 작성해주시길 바랍니다.
Milk.cs
using System;
namespace StackQueue
{
class Milk
{
public int ExpireAt { get; private set; }
public string Name { get; private set; }
public Milk(string inputName, int inputExpire)
{
Name = inputName;
ExpireAt = inputExpire;
}
}
}
VendingMachine.cs
using System;
using System.Collections.Generic;
namespace StackQueue
{
class VendingMachine
{
Queue<Milk> milkContainer = new Queue<Milk>();
// 우유 넣기 메서드
public void AddMilk(Milk milk)
{
milkContainer.Enqueue(milk);
Console.WriteLine($"머신에 넣은 우유: {milk.Name} / 유통기한 : {milk.ExpireAt}");
}
// 우유 꺼내기 메서드
public void RemoveMilk()
{
if ( milkContainer.Count > 0 )
{
Milk dequeuedMilk = milkContainer.Dequeue();
Console.WriteLine($"꺼낸 우유 : {dequeuedMilk.Name} / {dequeuedMilk.ExpireAt}");
Console.WriteLine("-------------------------------------------");
Console.WriteLine($"남아있는 우유 갯수: {milkContainer.Count}");
foreach ( Milk milk in milkContainer )
{
Console.WriteLine($"{milk.Name} 의 유통기한 : {milk.ExpireAt}");
}
Console.WriteLine("-------------------------------------------");
Console.WriteLine();
}
else
{
Console.WriteLine();
Console.WriteLine("남아있는 우유가 없소...");
Console.WriteLine();
}
}
}
}
Program.cs
using System;
namespace StackQueue
{
class Program
{
static void Main(string[] args)
{
VendingMachine machine = new VendingMachine();
Console.WriteLine();
Console.WriteLine("----------- 우유 넣기 테스트 -----------");
Console.WriteLine();
machine.AddMilk(new Milk("1번 우유", 25092424));
machine.AddMilk(new Milk("2번 우유", 25092512));
machine.AddMilk(new Milk("3번 우유", 25092622));
machine.AddMilk(new Milk("4번 우유", 25092703));
machine.AddMilk(new Milk("5번 우유", 25092800));
Console.WriteLine();
Console.WriteLine("----------- 우유 꺼내기 테스트 -----------");
Console.WriteLine();
machine.RemoveMilk();
machine.RemoveMilk();
machine.RemoveMilk();
machine.RemoveMilk();
machine.RemoveMilk();
machine.RemoveMilk();
machine.RemoveMilk();
}
}
}

큐를 이용해 우유가 들어간 순서대로 빠져나오는 것이 정상 동작하는 걸 확인할 수 있었다.
자가진단 심화 과제 ) 본인이 판단하는 자료구조

Shape라는 Enum을 만들어서 Spade, Heart, Clover, Diamond 네 가지를 요소로 가지게 합니다.
Card라는 클래스를 만듭니다. 위 열거형을 Card클래스 맴버변수로 가지게 해주세요. Card클래스에는 숫자를 나타내는 int형을 넣되, a는 1, J는 11, Q는 12, K는 13이라고 가정하겠습니다.
CardDeck이라는 클래스도 하나 제작하도록 하겠습니다. Card 객체를 52개 담을 수 있는 ???(자료구조) 들을 활용하여 두 개 만드시되, 하나는 unusedCards, 하나는 usedCards 의 이름으로 만들겠습니다.
CardDeck 클래스의 생성자에 unusedCards 속 52개의 Card를 중복없이 뉴할당해서 넣어주는 코드를 작성하여 주시기 바랍니다. 카드를 섞는 메소드를 작성하거나 생성자에서 바로 진행해주시기 바랍니다.
CardDeck 클래스에 아직 쓰지 않은 맨 위 카드를 보기만 하는 ShowTopCard 기능을 제작하여 주시기 바랍니다. 이는 맨 상단의 카드를 보기만 할 뿐 카드를 소모하진 않습니다. DrawCard라는 메소드도 하나 제작하겠습니다. 이 메소드는 아직 쓰지 않은 덱의 맨 위 카드를 반환받고 그와 동시에 usedCard에 쌓는 기능을 수행합니다.
Card.cs
using System;
namespace StackQueue
{
enum Shape
{
Spade, Heart, Clover, Diamond
}
class Card
{
public Shape Shape { get; private set; }
public int CardNum { get; private set; }
public Card(Shape inputShape, int inputNum)
{
Shape = inputShape;
CardNum = inputNum;
//Console.WriteLine($"{Shape} {CardNum} 카드 생성");
}
}
}
CardDeck.cs
using System;
using System.Collections.Generic;
using System.Linq;
namespace StackQueue
{
class CardDeck
{
Stack<Card> unusedCards = new Stack<Card>();
Stack<Card> usedCards = new Stack<Card>();
public CardDeck()
{
// 각 Shape x 12 카드 생성
// 이거 GetValues 런타임 중에 생각보다 무겁다 함. enum 끝에 End 두면 그게 Length값 ㄷㄷ
for (int i = 0; i < Enum.GetValues(typeof(Shape)).Length; i++)
{
for (int j = 1; j <= 13; j++)
{
unusedCards.Push(new Card((Shape)i, j));
}
}
// 생성된 카드 셔플
CardShuffle();
Console.WriteLine("카드 생성 및 셔플 완료");
}
public void ShowTopCard()
{
Console.WriteLine($"확인한 맨 위 카드: {unusedCards.Peek().Shape} {unusedCards.Peek().CardNum}");
}
public void DrawCard()
{
// unUserCards 맨 위 카드 빼고, usedCard에 쌓아
Card popedCard = unusedCards.Pop();
Console.WriteLine($"드로우 한 카드 : {popedCard.Shape} {popedCard.CardNum}");
usedCards.Push(popedCard);
}
// 카드 셔플 메서드
public void CardShuffle()
{
// 리스트로 변환
List<Card> cards = unusedCards.ToList();
Random rnd = new Random();
// 모든 카드를 랜덤한 위치와 자리 변경
for (int i = 0; i < cards.Count; i++)
{
int tempRnd = rnd.Next(0, cards.Count);
Card tempCard = cards[i];
cards[i] = cards[tempRnd];
cards[tempRnd] = tempCard;
}
// 덱에 적용
unusedCards = new Stack<Card>(cards.Reverse<Card>());
}
// 테스트를 위한 덱 전체 열람 메서드
public void ShowAllUnUsedCards()
{
Console.WriteLine();
Console.WriteLine($"-------------- 미열람 카드 전체 리스트 {unusedCards.Count} --------------");
foreach (Card card in unusedCards)
{
Console.WriteLine($"{card.Shape} {card.CardNum}");
}
Console.WriteLine("--------------------------------------------------");
Console.WriteLine();
}
public void ShowAllUsedCards()
{
Console.WriteLine();
Console.WriteLine($"-------------- 미열람 카드 전체 리스트 {usedCards.Count} --------------");
foreach (Card card in usedCards)
{
Console.WriteLine($"{card.Shape} {card.CardNum}");
}
Console.WriteLine("--------------------------------------------------");
Console.WriteLine();
}
}
}
Program.cs
using System;
namespace StackQueue
{
class Program
{
static void Main(string[] args)
{
CardDeck cardDeck = new CardDeck();
// 미열람 카드 조회
cardDeck.ShowAllUnUsedCards();
// 픽, 드로우 테스트
cardDeck.ShowTopCard();
cardDeck.DrawCard();
cardDeck.ShowTopCard();
cardDeck.DrawCard();
cardDeck.ShowTopCard();
cardDeck.DrawCard();
cardDeck.DrawCard();
cardDeck.DrawCard();
cardDeck.DrawCard();
cardDeck.DrawCard();
// 열람 카드 조회
cardDeck.ShowAllUsedCards();
// 미열람 카드 조회
cardDeck.ShowAllUnUsedCards();
}
}
}


의도한대로 잘 동작한다.
여기서 몇가지만 더 보고 가자
public void CardShuffle()
{
// 리스트로 변환
List<Card> cards = unusedCards.ToList();
Random rnd = new Random();
// 모든 카드를 랜덤한 위치와 자리 변경
for (int i = 0; i < cards.Count; i++)
{
int tempRnd = rnd.Next(0, cards.Count);
Card tempCard = cards[i];
cards[i] = cards[tempRnd];
cards[tempRnd] = tempCard;
}
// 덱에 적용
unusedCards = new Stack<Card>(cards.Reverse<Card>());
}
이 셔플 메서드는
cards 수 만큼 순환하면서 인덱스 i와, 랜덤 인덱스의 카드를 스왑시키는 코드다.
피셔예이츠와 비슷한데, 피셔예이츠에서는 균등한 분배를 위한 처리가 되어있으나, 오히려 이 편이 조금 더 현실적인 셔플에 가깝다 생각해서 이렇게 구현했다.
그리고 CardDeck.cs 쪽의
public CardDeck()
{
// 각 Shape x 12 카드 생성
for (int i = 0; i < (int)Shape.End; i++)
{
for (int j = 1; j <= 13; j++)
{
unusedCards.Push(new Card((Shape)i, j));
}
}
// 생성된 카드 셔플
CardShuffle();
Console.WriteLine("카드 생성 및 셔플 완료");
}
이 생성자 코드를 보면 Enum.GetValues(typeof(Shape)) 으로 Shape이 추가되는 상황에도 대응할 수 있게 작성했다.
하지만 GetValues는 런타임 도중에 작동하기에 다소 무거울 수 있어 더욱 최적화를 진행해봤다.
enum Shape
{
Spade, Heart, Clover, Diamond, End // End를 끝에 추가
}
열거형을 이렇게 구성하면 End 값이 Shape의 Length에 해당하는 값이 된다.
for (int i = 0; i < (int)Shape.End; i++)
for문을 이렇게 수정하는 것으로 최적화 할 수 있다.
Enum 값에 시작, 끝에 특정 값을 정의하는 것으로 이런 활용이 가능하다는 아이디어가 인상 깊었다.
'C#' 카테고리의 다른 글
| C# - 자료구조 : 트리 (Tree) & 그래프 (Graph) (1) | 2025.09.29 |
|---|---|
| C# - 덱 (Deque : Double Ended Queue) (0) | 2025.09.29 |
| C# - 딕셔너리 (Dictionary) (0) | 2025.09.26 |
| C# - 스택 (Stack), 큐 (Queue) (0) | 2025.09.26 |
| C# - BFS (너비 우선 탐색) / DFS (깊이 우선 탐색) (0) | 2025.09.25 |