첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
본 블로그는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 블로그 콘텐츠 향상을 위해 쓰여집니다.

728x90
반응형
728x170

TestProject.zip
다운로드

▶ MainForm.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading;
using System.Windows.Forms;

namespace TestProject
{
    /// <summary>
    /// 메인 폼
    /// </summary>
    public partial class MainForm : Form
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Structure
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 네이티브 메시지 - NativeMessage

        /// <summary>
        /// 네이티브 메시지
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct NativeMessage
        {
            /// <summary>
            /// 핸들
            /// </summary>
            public IntPtr Handle;

            /// <summary>
            /// 메시지
            /// </summary>
            public uint Message;

            /// <summary>
            /// WORD 매개 변수
            /// </summary>
            public IntPtr WordParameter;

            /// <summary>
            /// LONG 매개 변수
            /// </summary>
            public IntPtr LongParameter;

            /// <summary>
            /// 타임
            /// </summary>
            public uint Time;

            /// <summary>
            /// 포인트
            /// </summary>
            public Point Point;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Import
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Private

        #region 메시지 엿보기 - PeekMessage(message, handle, minimumFilter, maximumFilter, flag)

        /// <summary>
        /// 메시지 엿보기
        /// </summary>
        /// <param name="message">네이티브 메시지</param>
        /// <param name="handle">핸들</param>
        /// <param name="minimumFilter">최소 필터</param>
        /// <param name="maximumFilter">최대 필터</param>
        /// <param name="flag">플래그</param>
        /// <returns>처리 결과</returns>
        [SuppressUnmanagedCodeSecurity]
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool PeekMessage(out NativeMessage message, IntPtr handle, uint minimumFilter, uint maximumFilter, uint flag);

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// WM_MOUSEFIRST
        /// </summary>
        private const uint WM_MOUSEFIRST = 0x0200;

        /// <summary>
        /// WM_MOUSELAST
        /// </summary>
        private const uint WM_MOUSELAST = 0x020D;

        /// <summary>
        /// PM_REMOVE
        /// </summary>
        private const int PM_REMOVE = 0x0001;

        /// <summary>
        /// 지연 시간
        /// </summary>
        private const int DELAY = 250;

        /// <summary>
        /// 디스크 카운트
        /// </summary>
        private const int DISK_COUNT = 5;

        /// <summary>
        /// Y1 좌표
        /// </summary>
        private const int Y1 = 10;

        /// <summary>
        /// Y2 좌표
        /// </summary>
        private const int Y2 = 50;

        /// <summary>
        /// 디스크 두께
        /// </summary>
        private const int DISK_THICKNESS = 20;

        /// <summary>
        /// X1 좌표
        /// </summary>
        private int x1;
        
        /// <summary>
        /// X2 좌표
        /// </summary>
        private int x2;
        
        /// <summary>
        /// X3 좌표
        /// </summary>
        private int x3;
        
        /// <summary>
        /// Y3 좌표
        /// </summary>
        private int y3;

        /// <summary>
        /// 못 사각형 배열
        /// </summary>
        private Rectangle[] pegRectangleArray = new Rectangle[3];

        /// <summary>
        /// 못 스택
        /// </summary>
        private Stack<int>[] pegStack = new Stack<int>[3];

        /// <summary>
        /// 점유 못 카운트
        /// </summary>
        private int occupiedPegCount = 0;

        /// <summary>
        /// 이동 여부
        /// </summary>
        private bool isMoving = false;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainForm()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainForm()
        {
            InitializeComponent();
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 폼 로드시 처리하기 - Form_Load(sender, e)

        /// <summary>
        /// 폼 로드시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Form_Load(object sender, EventArgs e)
        {
            for(int i = 0; i < 3; i++)
            {
                this.pegStack[i] = new Stack<int>();
            }

            int diskWidth = (DISK_COUNT + 2) * DISK_THICKNESS;

            ClientSize = new Size(3 * diskWidth + 4 * DISK_THICKNESS, ClientSize.Height);

            this.x1 = (int)(DISK_THICKNESS + diskWidth / 2);
            this.x2 = this.x1 + diskWidth + DISK_THICKNESS;
            this.x3 = this.x2 + diskWidth + DISK_THICKNESS;
            this.y3 = ClientSize.Height - 10;

            this.pegRectangleArray[0] = new Rectangle(this.x1 - DISK_THICKNESS / 2, Y2, DISK_THICKNESS, this.y3 - Y2);
            this.pegRectangleArray[1] = new Rectangle(this.x2 - DISK_THICKNESS / 2, Y2, DISK_THICKNESS, this.y3 - Y2);
            this.pegRectangleArray[2] = new Rectangle(this.x3 - DISK_THICKNESS / 2, Y2, DISK_THICKNESS, this.y3 - Y2);

            for(int i = 0; i < DISK_COUNT; i++)
            {
                this.pegStack[0].Push(diskWidth);

                diskWidth -= DISK_THICKNESS;
            }

            this.occupiedPegCount = 0;
        }

        #endregion
        #region 폼 마우스 이동시 처리하기 - Form_MouseMove(sender, e)

        /// <summary>
        /// 폼 마우스 이동시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Form_MouseMove(object sender, MouseEventArgs e)
        {
            int pegUnderMouse = GetPeg(e.Location);

            if(pegUnderMouse < 0)
            {
                Cursor = Cursors.Default;
            }
            else
            {
                Cursor = Cursors.Cross;
            }
        }

        #endregion
        #region 폼 마우스 클릭시 처리하기 - Form_MouseClick(sender, e)

        /// <summary>
        /// 폼 마우스 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Form_MouseClick(object sender, MouseEventArgs e)
        {
            if(this.isMoving)
            {
                return;
            }

            int pegUnderMouse = GetPeg(e.Location);

            if(pegUnderMouse < 0)
            {
                return;
            }

            this.isMoving = true;

            Cursor = Cursors.WaitCursor;

            Application.DoEvents();

            int otherPeg = 0;

            if(this.occupiedPegCount != 1 && pegUnderMouse != 1)
            {
                otherPeg = 1;
            }

            if(this.occupiedPegCount != 2 && pegUnderMouse != 2)
            {
                otherPeg = 2;
            }

            MoveDisk(this.occupiedPegCount, pegUnderMouse, otherPeg, DISK_COUNT);

            this.occupiedPegCount = pegUnderMouse;

            Cursor = Cursors.Default;

            this.isMoving = false;

            FlushMouseMessages();
        }

        #endregion
        #region 폼 페인트시 처리하기 - Form_Paint(sender, e)

        /// <summary>
        /// 폼 페인트시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Form_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.Clear(BackColor);

            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

            DrawPeg(e.Graphics);

            DrawDisk(e.Graphics);
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Function

        #region 못 구하기 - GetPeg(location)

        /// <summary>
        /// 못 구하기
        /// </summary>
        /// <param name="location">위치</param>
        /// <returns>못 인덱스</returns>
        private int GetPeg(Point location)
        {
            if(this.isMoving)
            {
                return -1;
            }

            for(int i = 0; i < 3; i++)
            {
                if(this.pegRectangleArray[i].Contains(location))
                {
                    if(i == this.occupiedPegCount)
                    {
                        return -1;
                    }

                    return i;
                }
            }

            return -1;
        }

        #endregion
        #region 디스크 이동하기 - MoveDisk(fromPeg, toPeg, otherPeg, diskCount)

        /// <summary>
        /// 디스크 이동하기
        /// </summary>
        /// <param name="fromPeg">FROM 못</param>
        /// <param name="toPeg">TO 못</param>
        /// <param name="otherPeg">다른 못</param>
        /// <param name="diskCount">디스크 카운트</param>
        private void MoveDisk(int fromPeg, int toPeg, int otherPeg, int diskCount)
        {
            if(diskCount > 1)
            {
                MoveDisk(fromPeg, otherPeg, toPeg, diskCount - 1);
            }

            this.pegStack[toPeg].Push(this.pegStack[fromPeg].Pop());

            Refresh();

            Thread.Sleep(DELAY);

            if(diskCount > 1)
            {
                MoveDisk(otherPeg, toPeg, fromPeg, diskCount - 1);
            }
        }

        #endregion
        #region 마우스 메시지 제거하기 - FlushMouseMessages()

        /// <summary>
        /// 마우스 메시지 제거하기
        /// </summary>
        private void FlushMouseMessages()
        {
            NativeMessage message;

            while(PeekMessage(out message, IntPtr.Zero, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE));
        }

        #endregion

        #region 못 그리기 - DrawPeg(graphics)

        /// <summary>
        /// 못 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        private void DrawPeg(Graphics graphics)
        {
            for(int i = 0; i < 3; i++)
            {
                graphics.FillRectangle(Brushes.Orange, this.pegRectangleArray[i]);

                graphics.DrawRectangle(Pens.Red, this.pegRectangleArray[i]);
            }
        }

        #endregion
        #region 디스크 그리기 - DrawDisk(graphics)

        /// <summary>
        /// 디스크 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        private void DrawDisk(Graphics graphics)
        {
            for(int peg = 0; peg < 3; peg++)
            {
                int y = this.y3 - 2 - DISK_THICKNESS;

                foreach(int diskWidth in this.pegStack[peg].ToArray().Reverse())
                {
                    Rectangle diskRectangle = new Rectangle
                    (
                        this.pegRectangleArray[peg].X + DISK_THICKNESS / 2 - diskWidth / 2,
                        y,
                        diskWidth,
                        DISK_THICKNESS
                    );

                    graphics.FillRectangle(Brushes.LightGreen, diskRectangle);

                    graphics.DrawRectangle(Pens.Green, diskRectangle);

                    y -= DISK_THICKNESS + 2;
                }
            }
        }

        #endregion
    }
}
728x90
반응형
그리드형
Posted by 사용자 icodebroker

댓글을 달아 주세요