<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>mandarine_punch.log</title>
        <link>https://velog.io/</link>
        <description>개발을 좋아하는 귤나라 사람입니다.</description>
        <lastBuildDate>Thu, 11 Aug 2022 06:58:55 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>mandarine_punch.log</title>
            <url>https://images.velog.io/images/mandarine_punch/profile/5720107c-fd58-43da-a45c-0bb9d3587b79/캐릭터.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. mandarine_punch.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/mandarine_punch" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[BOJ-1083] 소트 - Java]]></title>
            <link>https://velog.io/@mandarine_punch/BOJ-1083-%EC%86%8C%ED%8A%B8-Java</link>
            <guid>https://velog.io/@mandarine_punch/BOJ-1083-%EC%86%8C%ED%8A%B8-Java</guid>
            <pubDate>Thu, 11 Aug 2022 06:58:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/1083">문제 링크</a></p>
</blockquote>
<p>오랜만에 코테 문제를 풀어서 감도 익힐겸 비교적 쉬워 보였던(?) 문제를 가져왔다.</p>
<hr>
<h2 id="✍-문제-설명">✍ 문제 설명</h2>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/1a3c0736-22be-4d51-bd87-04f38e151121/image.PNG" alt=""></p>
<hr>
<h2 id="📝-문제-풀이">📝 문제 풀이</h2>
<p>처음 문제를 보고 풀었을 때는 단순 버블 소트구나 생각이 들어 배열의 앞뒤 값만 바꾸어서 풀었다. 그런데 웬걸 테스트 통과가 되지 않는 것이었다. 그래서 반례를 생각해보니 교환 횟수를 간과하고 풀고 있었다는 것을 깨달았다.</p>
<p>예를 들어, 10개의 숫자에 17번의 교환 횟수가 있는 입력값이 들어왔다고 생각해보자.</p>
<pre><code class="language-java">input : 10
        1 2 3 4 5 6 7 8 9 10
        17</code></pre>
<p>이런 입력값이 들어왔을 때, 출력값은 아래와 같이 나와야 한다.</p>
<pre><code class="language-java">output : 10 9 1 2 3 4 5 6 7 8</code></pre>
<p>위 반례를 토대로 내가 푼 방식은 아래 설명과 같다.</p>
<ul>
<li>숫자가 담긴 list에서 교환 횟수로 도달할 수 있는 범위를 파악</li>
<li>파악한 범위에서 <strong>최댓값(max)</strong>과 <strong>최댓값의 인덱스(maxIdx)</strong>를 따로 저장</li>
<li>그 값을 list에서 제거한 뒤 앞으로 가져옴</li>
<li>미리 저장해둔 maxIdx와 현재 index를 이용해 교환횟수를 차감</li>
</ul>
<p>말로 설명하려니까 조금 난잡한 것 같다 ㅠㅠ. 아래 코드를 보면 조금 더 이해가 빠를 것이다.</p>
<hr>
<h2 id="✅-정답-코드">✅ 정답 코드</h2>
<h3 id="--코드-설명">- 코드 설명</h3>
<pre><code class="language-java">import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        // 숫자 개수
        int N = Integer.parseInt(br.readLine());
        ArrayList&lt;Integer&gt; list = new ArrayList&lt;&gt;();

        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 0; i &lt; N; i++) {
            list.add(Integer.parseInt(st.nextToken()));
        }

        // 교환 횟수
        int S = Integer.parseInt(br.readLine());

        // 1. 교환 횟수가 남아있고 i가 N보다 작다면
        // (원래는 if(S &lt;= 0) break; 라는 로직이었지만 for문 조건에 합쳤다.)
        for (int i = 0; i &lt; N &amp;&amp; 0 &lt; S; i++) {
            int max = 0, maxIdx = 0;

            // 2. 현재 index(i)로부터 교환 횟수가 남아있고 j가 N보다 작다면
            for (int j = i; j &lt; N &amp;&amp; j &lt;= S + i; j++) {
                if(max &lt;= list.get(j)) {
                    // 3. 해당 범위에서 최댓값 추출
                    max = list.get(j);
                    maxIdx = j;
                }
            }                

            // 4. 해당 위치 최댓값을 현재 위치에 할당
            list.remove(maxIdx);
            list.add(i, max);

            // 5. 최댓값 index에서 현재 index의 차는 곧 값이 이동한 횟수이므로
            // 교환 횟수를 이동한 횟수만큼 차감
            S -= (maxIdx - i);
        }

        // 값 출력
        StringBuilder sb = new StringBuilder();
        for (int num : list) {
            sb.append(num).append(&quot; &quot;);
        }

        System.out.println(sb.toString());
    }
}</code></pre>
<h3 id="--순수-코드">- 순수 코드</h3>
<pre><code class="language-java">import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int N = Integer.parseInt(br.readLine());
        ArrayList&lt;Integer&gt; list = new ArrayList&lt;&gt;();

        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 0; i &lt; N; i++) {
            list.add(Integer.parseInt(st.nextToken()));
        }

        int S = Integer.parseInt(br.readLine());

        for (int i = 0; i &lt; N &amp;&amp; 0 &lt; S; i++) {
            int max = 0, maxIdx = 0;

            for (int j = i; j &lt; N &amp;&amp; j &lt;= S + i; j++) {
                if(max &lt;= list.get(j)) {
                    max = list.get(j);
                    maxIdx = j;
                }
            }                

            list.remove(maxIdx);
            list.add(i, max);
            S -= (maxIdx - i);
        }

        StringBuilder sb = new StringBuilder();
        for (int num : list) {
            sb.append(num).append(&quot; &quot;);
        }

        System.out.println(sb.toString());
    }
}</code></pre>
<hr>
<h2 id="🎈-후기">🎈 후기</h2>
<p>빨리 풀 수 있을 줄 알았는데, 의외로 시간이 좀 걸렸다 ㅋㅋ. 알고리즘은 역시 꾸준히 공부해야 안까먹나보다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Project] NNgame - 게임 판매 사이트]]></title>
            <link>https://velog.io/@mandarine_punch/Project-NNgame-%EA%B2%8C%EC%9E%84-%ED%8C%90%EB%A7%A4-%EC%82%AC%EC%9D%B4%ED%8A%B8</link>
            <guid>https://velog.io/@mandarine_punch/Project-NNgame-%EA%B2%8C%EC%9E%84-%ED%8C%90%EB%A7%A4-%EC%82%AC%EC%9D%B4%ED%8A%B8</guid>
            <pubDate>Mon, 04 Jul 2022 16:06:50 GMT</pubDate>
            <description><![CDATA[<p>그동안 프로젝트 때문에 정신이 없어서 블로그에 소홀했지만,
프로젝트가 끝났기에! 그 기록을 남겨두려 한다.</p>
<hr>
<h2 id="✍-만들게된-계기">✍ 만들게된 계기</h2>
<p>국비지원 학원 첫 팀 프로젝트..!이다 ㅎㅎ.
어떤 것을 만들어볼까? 하다 여러 의견이 나왔었다.</p>
<p><strong>조원1</strong> - 공기업 페이지 =&gt; 중요 정보를 잘 간추렸으면 좋겠다
<strong>조원2</strong> - 커스텀 검색 페이지 =&gt; 구글의 장점(방대한 정보량)과 네이버의 장점(블로그)을 취합
<strong>조원3</strong> - 박물관 전시 예약사이트 =&gt; 입장권을 사고팔 수 있음
<strong>조원4</strong> - 국비지원 학원 홈페이지를 업그레이드
<strong>조원5</strong> - 로켓펀치 같은 it관련 취업페이지
<strong>조원6</strong> - 정보 전달 커뮤니티
<strong>조장 겸 본인</strong> - 게임 판매 사이트<span style="color: tomato"> (당첨!)</span> =&gt; 타 사이트들의 불편한 접근성을 보완</p>
<p>각자 왜 이런 페이지를 만들고 싶었는지에 대한 어필 시간을 가진 후, 최종적으로 나의 의견이 채택됐다. (코로나 가불기와 게임에 대한 애정이 느껴져서였을까..? ㅋㅋ)</p>
<hr>
<h2 id="📆-프로젝트-기간">📆 프로젝트 기간</h2>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/4ab1a83f-cf67-43a1-ba0e-d0a26e784d1e/image.PNG" alt=""></p>
<hr>
<h2 id="🔨-개발-도구-및-환경">🔨 개발 도구 및 환경</h2>
<p><strong>Front -</strong> HTML, CSS, JS, jQuery, JSP(Model2)
<strong>Back -</strong> Java(1.8v)
<strong>DB -</strong> ORACLE 11g
<strong>Server -</strong> Apache Tomcat(8.5v)
<strong>IDE -</strong> eclipse
<strong>DB 관리 툴 -</strong> DBeaver
<strong>프레임워크 -</strong> MyBatis(DB), Bootstrap(CSS)
<strong>형상 관리 -</strong> Github</p>
<hr>
<h2 id="📝-user-flow--erd">📝 User Flow &amp; ERD</h2>
<h3 id="--비로그인시-user-flow">- 비로그인시 User Flow</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/5a907f3c-c6b5-40fb-9037-96120de6f7a6/image.png" alt=""></p>
<h3 id="--로그인시-user-flow">- 로그인시 User Flow</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/af2e3f44-1e00-4b74-8622-ee0823358a13/image.png" alt=""></p>
<h3 id="--erd">- ERD</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/904ef460-adba-403a-9320-2a295dae3a8d/image.png" alt=""></p>
<hr>
<h2 id="🎈-주요-기능-소개">🎈 주요 기능 소개</h2>
<h3 id="--메인-화면">- 메인 화면</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/2b64516b-ea33-4d84-99d2-c09afb59f25a/image.gif" alt="메인 gif"></p>
<ul>
<li>슬라이더를 이용해 메인 게임을 보여줬으며, 할인, 인기, 추천 게임으로 이동이 가능하다.</li>
</ul>
<h3 id="--회원-가입">- 회원 가입</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/d84ab6da-b380-4309-885f-b14cf6628c2b/image.gif" alt="회원가입 gif"></p>
<ul>
<li>JS로 값에 대한 <strong>validation check</strong>를 해줬다.</li>
<li><strong>DB</strong>에 이미 존재하는 유저일 경우, 중복 가입이 불가하고 그렇지 않을 경우는 정상적으로 회원 가입이 된다.</li>
<li><strong>DB</strong>에 저장되는 모든 비밀번호는 <strong>SHA-256</strong> 해시 알고리즘과 <strong>임의의 데이터(salt)</strong> 를 추가하여, 암호화해 넣어줬다.</li>
</ul>
<h4 id="db에-저장된-유저-정보">DB에 저장된 유저 정보</h4>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/14c555f2-7691-4e84-9489-abe7e7a48b3d/image.PNG" alt=""></p>
<h3 id="--로그인">- 로그인</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/88ee23be-fbaa-4080-a73a-2d72f18f3178/image.gif" alt="로그인 gif"></p>
<ul>
<li>카카오 및 네이버 연동 로그인을 구현했다.</li>
<li>유저가 로그인에 성공하면 <strong>session</strong>에 유저 정보를 넣어 로그인 상태를 유지했다.</li>
</ul>
<h3 id="--비밀번호-찾기">- 비밀번호 찾기</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/95f8631b-c507-4b11-9ce0-80f98916cfb9/image.gif" alt="비밀번호 찾기 gif"></p>
<ul>
<li>DB를 순회하여 해당 유저의 정보가 존재하면, 비밀번호를 임의의 난수로 바꾸고,
바꾼 비밀번호를 <strong>네이버 SMTP</strong>를 이용해 메일로 보내줬다.</li>
</ul>
<h3 id="--마이-페이지연동-로그인">- 마이 페이지(연동 로그인)</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/d0921b0b-ec18-4739-8335-d6b290f1029b/image.gif" alt="마이 페이지(연동 로그인) gif"></p>
<ul>
<li><strong>kakao</strong>와 <strong>naver</strong>일 때를 구분해줬으며, 닉네임은 언제든지 수정 가능하다.</li>
<li>단, 연동 로그인은 원클릭 로그인이므로 비밀번호가 따로 필요하지 않아, <strong>비밀번호를 수정할 수 없게 구현</strong>했다.</li>
</ul>
<h3 id="--마이-페이지일반-로그인">- 마이 페이지(일반 로그인)</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/e29621fe-0f44-47ef-bf72-3849e1dfb894/image.gif" alt="마이 페이지(일반 로그인) gif"></p>
<ul>
<li>연동 로그인과 달리 <strong>비밀번호를 변경할 수 있다</strong>.</li>
</ul>
<h3 id="--회원-탈퇴">- 회원 탈퇴</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/9caab776-d969-401e-81d2-978304da64d9/image.gif" alt=""></p>
<ul>
<li>현재 로그인된 유저의 비밀번호를 한 번 더 입력하면 탈퇴할 수 있게 구현했다.</li>
<li>연동 로그인시는 연동 로그인할 때의 고정 비밀번호를 부여해줬으며, 회원탈퇴를 누르면 정상적으로 탈퇴된다.</li>
</ul>
<h3 id="--스토어">- 스토어</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/70049efd-f172-4a53-8217-c77fbc64ae1d/image.gif" alt=""></p>
<ul>
<li>장르 별로 분류가 가능하다.</li>
<li>원하는 정렬 순서(사전순, 할인 게임, 가격(오름차순, 내림차순))로 정렬도 가능하다</li>
</ul>
<h3 id="--스토어검색">- 스토어(검색)</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/3c9cff8c-63c5-4424-aa03-c68611153b29/image.gif" alt=""></p>
<ul>
<li><strong>like문법</strong>을 써서 DB에서 해당 키워드를 불러왔다.</li>
<li>검색한 게임 내에서도 원하는 정렬 순서 선택이 가능하다.</li>
</ul>
<h3 id="--게임-상세리뷰-작성-및-삭제">- 게임 상세(리뷰 작성 및 삭제)</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/4f4e5623-6b28-4a0a-958a-c58f3b3c2ce7/image.gif" alt=""></p>
<ul>
<li>접속한 아이디로 리뷰 작성이 가능하다.</li>
<li>또한, 본인이 작성한 글일 경우에만 삭제가 가능하게 구현했다.</li>
</ul>
<h3 id="--장바구니">- 장바구니</h3>
<h4 id="장바구니-게임-추가">장바구니 게임 추가</h4>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/1ac339f8-abbd-4ede-aa10-62d2f465d51b/image.gif" alt=""></p>
<h4 id="장바구니-게임-중복-체크">장바구니 게임 중복 체크</h4>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/f06c7c44-b8ed-47fa-9726-366d72abc6f4/image.gif" alt=""></p>
<ul>
<li>로그인시에만 접근 가능하다.</li>
<li>원하는 게임을 개수 제한 없이 담을 수 있고, 담은 게임을 삭제할 수도 있다.</li>
<li>DB를 체크하여 이미 담겨 있는 게임일 경우, 다시 담지 못하게 구현했다.</li>
</ul>
<h3 id="--결제-관리">- 결제 관리</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/1e6ae880-a5ee-4a2d-b9a3-3a21236cd204/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/d09c62bb-94e8-4896-a07e-c824c7fe699f/image.png" alt=""></p>
<ul>
<li>로그인시에만 접근 가능하다.</li>
<li><strong>2가지 방법(바로 구매, 장바구니)</strong>으로 들어올 수 있다.</li>
<li>바로 구매시 해당 게임이 라이브러리로 들어간다.</li>
<li>장바구니에서 구매시 장바구니에 있는 게임 개수만큼 라이브러리에 들어가며, 장바구니는 비워진다. </li>
</ul>
<h3 id="--라이브러리">- 라이브러리</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/f730c3b9-4a4d-4519-9637-134f9aa4561c/image.png" alt=""></p>
<ul>
<li>로그인시에만 활성화되고 결제가 정상적으로 완료된 게임이 담겨있다.</li>
<li>스토어와 마찬가지로 라이브러리 내에서도 검색 기능을 구현하여, 원하는 게임을 키워드로 찾을 수 있다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/ce7c98e8-0dfe-4645-b305-4acff99061ad/image.gif" alt=""></p>
<ul>
<li>위는 라이브러리에 이미 존재하는 게임일 경우의 값 체크이다.</li>
</ul>
<h3 id="--장바구니---결제---라이브러리">- 장바구니 -&gt; 결제 -&gt; 라이브러리</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/3019ae4b-2818-4189-b7da-a5a382fe9c07/image.gif" alt=""></p>
<ul>
<li>위에서 설명한 게임 구매의 흐름이다.</li>
</ul>
<h3 id="--고객-지원페이징">- 고객 지원(페이징)</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/fcc75516-ba73-44ba-ba5b-2abcfcb10683/image.png" alt=""></p>
<ul>
<li>문의형 게시판이다.</li>
<li>한 페이지에 10개의 글을 보여주고 한 화면에 5개의 페이지만 보이게 제한했다.</li>
<li>다음 버튼을 누르면 다음 페이지의 첫 번째 넘버(1~5p -&gt; 6p)</li>
<li>이전 버튼을 누르면 이전 페이지의 마지막 넘버로 오게 구현했다.(5p &lt;- 6~10p)</li>
</ul>
<h3 id="--고객-지원로그인-여부--글-상세">- 고객 지원(로그인 여부 &amp; 글 상세)</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/b9cd6e41-6fff-446a-9b6b-9ff76309fa2a/image.png" alt=""></p>
<ul>
<li>로그인시에 글 작성이 가능하다.</li>
<li>본인 글이 아닐 때는 글 수정 및 삭제가 불가하다.</li>
</ul>
<h3 id="--고객-지원글-작성--글-수정">- 고객 지원(글 작성 &amp; 글 수정)</h3>
<h4 id="글-작성">글 작성</h4>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/4da5f663-d89c-4ab2-a4ca-7c630a2f7e63/image.gif" alt=""></p>
<ul>
<li>문의, 제목, 내용 중 한 곳이라도 공란일 경우 작성할 수 없게 구현했다.</li>
</ul>
<h4 id="글-수정--삭제">글 수정 &amp; 삭제</h4>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/7d0603a2-65a0-4d0c-a8b4-c6b3f401f09c/image.gif" alt=""></p>
<h4 id="닉네임-업데이트">닉네임 업데이트</h4>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/9e42eb1e-98e2-45a2-a973-6bf0574c243c/image.gif" alt=""></p>
<ul>
<li>유저가 닉네임을 변경하면 그에 맞춰 고객지원의 작성자도 update문을 날려 닉네임이 바뀌게 구현해줬다.</li>
</ul>
<hr>
<h2 id="😭-후기">😭 후기</h2>
<p>어찌어찌 끝이 났다!</p>
<p>학원에서 JSP와 웹에 대해 들어가지 않은 채 프로젝트를 줘서…. 처음에는 정말 막막했다. <strong>(심지어 조장..!)</strong>
그래도 하고자 하는 의지가 컸기에, 처음에 느꼈던 막막함은 금세 사라지고 어느새 <strong>&#39;조원들과 얼른 구현하고 싶다&#39;</strong> 라는 마음만 남아 들떴었다.</p>
<p>그리고 첫 회의..!</p>
<p>조원들과 매일 회의를 하자는 규칙을 정하고, 회의 끝에는 다음 회의 목표를 정하면서 마무리했다.
이런 방식 덕분에 매 회의 때마다 도돌이표 되는 내용이 없었고 항상 한 걸음 더 나아갔다.</p>
<p>열심히는 하지만 이해를 잘 못 하는 조원들도 있었는데,
그 조원들에 있어서는 마감 기한 전까지 최대한 이해시키며 홀로 구현할 수 있게 도와줬고
기한이 임박했지만 완벽하게 구현이 안된 영역은 내가 따로 구현한 뒤, 
해당 파트를 맡은 조원이 가능한 시간대에 구현한 파트별로 세세히 설명해줬다.</p>
<p>조원들에게 설명을 해주면서 나도 배우는 점이 많았고, <strong>서로 발전</strong>해 나간다는 게 너무 좋았다.</p>
<p>이렇듯 다들 열심히 해준 덕분에, 정말 만족스럽게 프로젝트를 끝낼 수 있었다.</p>
<p>한 프로젝트의 조장이 처음이라 많이 부족했지만 그럼에도 잘 따라와준 조원분들에게 정말 정말 감사한 마음뿐이다.</p>
<blockquote>
<p><a href="https://github.com/MandarinePunch/NNgame-project">Github - NNgame 프로젝트</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Useful] StringBuilder.append(char[] arr) - Java]]></title>
            <link>https://velog.io/@mandarine_punch/Useful-StringBuilder.appendchar-str-Java</link>
            <guid>https://velog.io/@mandarine_punch/Useful-StringBuilder.appendchar-str-Java</guid>
            <pubDate>Mon, 16 May 2022 07:24:45 GMT</pubDate>
            <description><![CDATA[<p><strong>StringBuilder</strong>에 <strong>char 배열</strong>을 <strong>append</strong>할 경우,
char 배열 인자 전체가 <strong>문자열</strong>로 추가됨</p>
<pre><code class="language-java">StringBuilder sb = new StringBuilder();

char[] alphabet = {&#39;a&#39;, &#39;b&#39;, &#39;c&#39;};

sb.append(alphabet);

System.out.println(sb); // abc</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ-2613] 숫자 구슬 - Java]]></title>
            <link>https://velog.io/@mandarine_punch/BOJ-2613-%EC%88%AB%EC%9E%90-%EA%B5%AC%EC%8A%AC-Java</link>
            <guid>https://velog.io/@mandarine_punch/BOJ-2613-%EC%88%AB%EC%9E%90-%EA%B5%AC%EC%8A%AC-Java</guid>
            <pubDate>Thu, 28 Apr 2022 08:32:22 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/2613">문제 링크</a></p>
</blockquote>
<p>쉬운듯 아닌듯 내 기준에 굉장히 까다로웠던 문제라서 기록하려 한다.</p>
<hr>
<h2 id="✍-문제-설명">✍ 문제 설명</h2>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/2b21a241-a3b5-4436-8532-d9a663ab14fe/image.PNG" alt=""></p>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/4627906e-2412-4085-bf4d-e810c3b0d490/image.PNG" alt=""></p>
<hr>
<h2 id="📝-문제-풀이">📝 문제 풀이</h2>
<p>처음 접했을 때는 최댓값이 최소가 되도록 그룹을 묶는 방법이 생각이 안났다.
<strong>&#39;주어진 그룹 수(M)로 나눠볼까?&#39;</strong> 라는 생각도 잠시, 나눠지기만 할뿐 최소합을 찾기란 쉽지 않았다.</p>
<p>그러다 밑에 있는 알고리즘 힌트를 봤는데, 엥? 이분 탐색이 있는 것이었다.
정렬 할 수 있는 문제도 아니고, 주어진 수의 범위가 적은데도 불구하고 말이다.</p>
<p>다시 오랜 시간 생각하니,
이분 탐색으로 구슬 묶음(mid와 구슬의 합을 비교하여 묶음) 마다 count를 하여 구슬 묶음과 비교하면 되겠다는 생각이 났다.</p>
<ul>
<li>만약 count가 구슬 묶음(M)보다 클 경우는 측정한 타겟 값이 정답 값보다 작은 것이므로<pre><code class="language-java">if(count &gt; M) lower = mid + 1; // 최소치를 올려 다시 최댓값 측정</code></pre>
</li>
<li>반대의 경우에는 측정한 타겟 값이 정답 값보다 큰 것이므로
(M과 count의 값이 같더라도 그 중에서 최소를 찾아야되기 때문에 최대치를 내려 재탐색한다)<pre><code class="language-java">if(count &lt;= M) upper = mid - 1; // 최대치를 내려 다시 최댓값 측정</code></pre>
</li>
</ul>
<p>이런 식으로 탐색이 완료되면 결국 구슬 묶음(M) 만큼의 최솟값이 나오게 된다.
(찾아보니 이렇게 이분 탐색으로 주어진 범위에서 최적해를 찾는 것을 Parametric Search라 한다고 한다)</p>
<p>이렇게 한 고비가 넘어가고 두 번째 고비가 찾아왔다 ㅋㅋ.
사실 이분 탐색으로 최적해를 찾는 것이 이 문제의 핵심이지만.. 나의 모자람이.. 부족한 꼼꼼함이.. 문제를 푸는 데에 있어 파국으로 가져왔다.</p>
<p>묶은 구슬이 어떻게 묶였는지 여부도 출력해야 하는데, 반례를 너무 가볍게 생각한 나머지 올바르게 출력시키는데도 꽤 많은 시간이 소요됐다.. (도저히 반례를 모르겠어서 다른 분들의 코드를 참조했다)</p>
<p>그리고 결국 해냈다.</p>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/1744fa65-f072-4361-b739-d35bd54691c6/image.PNG" alt=""></p>
<hr>
<h2 id="✅-정답-코드">✅ 정답 코드</h2>
<pre><code class="language-java">import java.util.*;
import java.io.*;

public class Main {
    static int[] beads;
    static int N, M;
    static int lower = 0, upper = 0;

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        N = Integer.parseInt(st.nextToken()); // 구슬 수
        M = Integer.parseInt(st.nextToken()); // 묶음 수

        beads = new int[N];

        st = new StringTokenizer(br.readLine());
        for (int i = 0; i &lt; N; i++) {
            beads[i] = Integer.parseInt(st.nextToken());
            lower = Math.max(lower, beads[i]);     // 구슬 합의 최소 범위
            upper += beads[i];                     // 구슬 합의 최대 범위
        }

        binarySearch();                // 이분 탐색으로 값 찾기
        System.out.println(lower);     // 구슬 합의 최댓값 출력

        StringBuilder sb = new StringBuilder();
        int cnt = 0;
        int sum = 0;

        for (int i = 0; i &lt; N; i++) {
            sum += beads[i];
            if (sum &gt; lower) {     // 만약 구슬 합이 최댓값보다 크면
                M--;            // 묶음이 하나 만들어진 것이므로 M--;
                sum = beads[i];
                sb.append(cnt).append(&quot; &quot;); // 현재 구슬 수를 sb에 추가
                cnt = 1;                     // 다시 구슬 수를 1로 카운트
            } else {
                cnt++;
            }

            if(M == N - i) break; // 묶음 수가 1인 경우를 세어 주기 위해
        }

        // 현재 M == (남은 묶음 수) 이므로
        // 구슬이 다 묶일 때까지 M--;
        while(M-- &gt; 0) {
            // 위에 for문에서 최댓값에 도달하지 못한 합의 구슬 수를 먼저 추가
            sb.append(cnt).append(&quot; &quot;);
            // 그 다음은 다 1씩 묶음이므로 
            cnt = 1;
        }

        System.out.println(sb);
    }

    static void binarySearch() {
        int mid = 0;
        while (upper &gt;= lower) {
            // 1. 범위 내의 mid값을 특정(구슬 합의 최댓값)
            mid = (upper + lower) / 2;

            // 3. mid 값에 대한 구슬 묶음 수
            int cnt = cntBeadsBundle(mid);

            // 4. 묶음 수(cnt)와 묶여야할 수(M)를 비교
            if (cnt &gt; M) {
                lower = mid + 1;
            } else {
                upper = mid - 1;
            }
        }
    }

    static int cntBeadsBundle(int mid) {
        int sum = 0;
        int cnt = 1;

        // 2. 범위 내의 중간 값(mid)과 구슬 합을 비교하여 cnt를 센다
        for (int i = 0; i &lt; N; i++) {
            sum += beads[i];
            if (sum &gt; mid) {
                cnt++;
                sum = beads[i];
            }
        }

        return cnt;
    }
}</code></pre>
<hr>
<h2 id="🎈-후기">🎈 후기</h2>
<p>이분 탐색에 대한 고정관념(방대한 수의 탐색만을 위해 써야징)이 있으면 접근조차 너무 어려웠던 문제였다. 이 문제를 시작으로 Parametric Search에 대해 좀 더 알아봐야겠다. 
푸는 데에 너무 오래 걸린 감이 있지만, 그래도 해냈다는데에 의의를 둬야겠다.😆</p>
<blockquote>
<p><a href="https://marades.tistory.com/7">Parametric Search 관련글</a> &lt;- 문제 푸는 데에 도움이 많이 됐다!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Useful] Integer.bitCount - Java]]></title>
            <link>https://velog.io/@mandarine_punch/Useful-Integer.bitCount-Java</link>
            <guid>https://velog.io/@mandarine_punch/Useful-Integer.bitCount-Java</guid>
            <pubDate>Tue, 19 Apr 2022 10:25:25 GMT</pubDate>
            <description><![CDATA[<h3 id="integerbitcountint-num">Integer.bitCount(int num)</h3>
<ul>
<li><p>num의 값을 binary로 변환 후, 1의 개수를 return 해준다.</p>
</li>
<li><p>num = 3 이면, 3 -&gt; 11(2)로 변환되므로 2를 return. 
num = 13 이면, 13 -&gt; 1101(2)로 변환되므로 3을 return.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[My toy] 블랙 잭 - Java]]></title>
            <link>https://velog.io/@mandarine_punch/My-Toy-%EB%B8%94%EB%9E%99-%EC%9E%AD-Java</link>
            <guid>https://velog.io/@mandarine_punch/My-Toy-%EB%B8%94%EB%9E%99-%EC%9E%AD-Java</guid>
            <pubDate>Mon, 18 Apr 2022 09:33:04 GMT</pubDate>
            <description><![CDATA[<h2 id="✍-주제-선정">✍ 주제 선정</h2>
<p>발표를 마친지는 일주일 정도 지났지만! 첫 번째 개인 프로젝트를 기록해보려 한다.
주제는 자유이며, <strong>&#39;배운 내용을 토대로 원하는 작품을 만들어봐라!&#39;</strong> 라고 지시를 받았다.
주제를 받자마자 든 생각은, <strong>&#39;재밌게 구현할 수 있는게 뭐가 있을까?&#39;</strong> 하는 생각이었다.
그리고 예전 미니 게임에서 블랙잭을 즐겁게 했던 기억이 떠올라, 블랙잭을 직접 만들어보기로 했다.</p>
<hr>
<h2 id="📝-게임-규칙">📝 게임 규칙</h2>
<p>구현을 위해 우선, 게임 규칙을 알아야 할 필요가 있었다.
규칙을 찾아보니... 생각보다 복잡했다.</p>
<p>처음 받은 두 장의 카드가 같은 숫자면 스플릿(각각의 카드로 취급하여 분할하여 게임 진행 가능)을 할 수 있다느니, A는 자신이 원할 때, 1 또는 11로 바꿔 쓸 수 있다느니 등등..
모두 구현하기에는 시간이 빠듯할 거 같았다.</p>
<p>그래도 내가 직접 플레이하면서 느낀 블랙잭의 재미는 없애고 싶지 않아,
재미는 최대한 유지한 상태로 규칙을 조금 수정하였다.
수정한 규칙은 발표 때 사용했던 ppt를 통해 설명해보겠다.</p>
<p align="center"><img src= "https://velog.velcdn.com/images/mandarine_punch/post/9b6075c8-8cfc-4aed-86ae-c4043b03bab8/image.JPG" width="50%" height="50%"/></p>

<ul>
<li><p>게임이 시작되면 <strong>Deck(카드 뭉치)</strong>이 만들어지고, 딜러와 플레이어에게 각각 2장의 카드가 지급된다.</p>
</li>
<li><p>Deck에 52장의 카드를 모두 넣어줘야 하는데,
뭔가.. 구현할 건 2인 정원 게임인데 카드 카운팅 의미도 없을 것 같고 해서 Deck 매수를 (1~10)의 카드 30장으로 지정해 줬다.</p>
</li>
</ul>
<p align="center"><img src= "https://velog.velcdn.com/images/mandarine_punch/post/966782c4-235b-44c6-92c2-1064c86def4e/image.JPG" width="50%" height="50%"/></p>

<ul>
<li><p>만약 이 두 장의 카드가 A 또는 10일 경우 <strong>BlackJack</strong>이 되어 승리하게 되는데,
이 규칙은 딜러와 플레이어 모두에게 적용되며,
낮은 확률로 둘 다 <strong>BlackJack</strong>이 나왔을 경우는 무승부가 된다.</p>
</li>
<li><p><strong>BlackJack</strong>으로 승리했을 경우, 배팅 금액의 1.5배를 받으며,
그 외의 방법으로 승리 시 배팅 금액의 2배를 받는다.</p>
</li>
<li><p>본 게임에서는 A를 숫자 1로 고정해주었다.</p>
</li>
</ul>
<p align="center"><img src= "https://velog.velcdn.com/images/mandarine_punch/post/e713a056-c899-4408-9dd6-067ab788bf40/image.JPG" width="50%" height="50%"/></p>

<ul>
<li><p><strong>BlackJack</strong>이 아닐 경우, 플레이어는 <strong>Hit(카드를 받는다)</strong>, <strong>Stay(그만 받는다)</strong> 중 하나를 선택할 수 있다.</p>
</li>
<li><p><strong>Stay</strong>를 선택할 경우 그 판에서는 더이상 카드를 받을 수 없으므로, 게임이 종료된다.</p>
</li>
</ul>
<p align="center"><img src= "https://velog.velcdn.com/images/mandarine_punch/post/c7c8a33d-470d-48f7-a02e-0eda3517f7d4/image.JPG" width="50%" height="50%"/></p>

<ul>
<li><p><strong>Hit</strong>를 선택할 시, 카드를 한 장 더 받게되고
이 때, 카드의 합이 <strong>21이 초과</strong>했을 경우 <strong>Bust</strong>로 딜러의 카드 합 및 결과와 관계없이 패배한다.</p>
</li>
<li><p><strong>21이하</strong>인 경우는, 본인 판단하에 <strong>Stay</strong>를 선택해 게임을 종료하여
딜러의 카드 합과 플레이어의 카드 합 중 높은 쪽이 승리하게 되는 게임이다.</p>
</li>
</ul>
<p align="center"><img src= "https://velog.velcdn.com/images/mandarine_punch/post/f40f727b-b905-47d4-b126-7a3cc476030c/image.JPG" width="50%" height="50%"/></p>

<ul>
<li><p>단, 딜러는 <strong>한 가지 제약 사항</strong>이 있는데, 카드의 합이 17이상일 경우, 카드를 더 이상 얻지 못한다는 규칙이다. 다르게 말하면, 딜러는 무조건 17이상이 될 때까지 카드를 뽑아야만 한다는 의미이다. (Bust가 날 확률이 높아진다)</p>
</li>
<li><p>찾아본 결과, 블랙잭은 플레이어에게 조금 유리하도록 만들어진(현실은 다를수도..?) 게임이라 딜러에게 이러한 제약 사항을 둔 것이다.</p>
</li>
</ul>
<hr>
<h2 id="🔨-구현">🔨 구현</h2>
<p>이제 규칙을 알았으니, 구현을 해야한다.
공책에 전체적인 알고리즘을 메모해가며, 큰 틀에서 작은 틀로 구현해나갔다.
아래 ppt는 전체 알고리즘 중 user(Player)의 알고리즘을 간추려 그려놓은 것이다.</p>
<p align="center"><img src= "https://velog.velcdn.com/images/mandarine_punch/post/c1461ae8-119e-4984-a48c-80c316eabded/image.JPG" width="50%" height="50%"/></p>

<pre><code class="language-java">public class BlackJack {
    // 카드 배분 및 Hit시 랜덤으로 덱에서 빼기 위해 Random을 선언
    private Random random = new Random();
    private Scanner scan = new Scanner(System.in); 
    private List&lt;Integer&gt; deck = new LinkedList&lt;&gt;();            // 카드 덱
    private List&lt;Integer&gt; userCntCard = new LinkedList&lt;&gt;();        // user 보유 카드
    private List&lt;Integer&gt; dealerCntCard = new LinkedList&lt;&gt;();    // dealer 보유 카드
    private int userSum = 0;    // user 카드 합
    private int dealerSum = 0;    // dealer 카드 합
    private int money = 0;        // 배팅 금액
    private final int BLACK_JACK_NUM = 21;
    private final int DEALER_MIN_NUM = 17;
}</code></pre>
<p>변수들을 위와 같이 선언하였다.
Deck List를 LinkedList로 만든 이유는 랜덤하게 카드를 덱에서 제거해야 하는데,
이럴 경우는 LinkedList가 ArrayList보다 더 효율적이라 판단하여 이렇게 구현하였다.
이렇게 적은 수에서는 거의 차이가 없겠지만 어쨋든...
이 둘의 차이는 나중에 포스팅 해보려고한다! 😁</p>
<p>간단히 말하자면, list 중간에 값이 더해지거나 없어질 경우,</p>
<ul>
<li>LinkedList는 해당 index의 앞 뒤 index를 이용해 list에 값을 더하거나 제거하지만,</li>
<li>ArrayList는 해당 index로부터 뒷 부분 전체를
추가하거나 제거할 값의 개수만큼 이동시켜 list에 값을 추가 및 제거한다.</li>
</ul>
<p>이제 본론으로 돌아와서,
모든 scan 입력 값은 아래 코드와 같이 예외처리를 해줬다.</p>
<pre><code class="language-java">public void playGame() {
    ...
    while(true) {
        System.out.println(&quot;1.게임 시작  2.게임 종료&quot;);
        selectNum = selectInput();
    ...
}

private int selectInput() {
    int selectNum;
    while (true) {
        try {
            selectNum = Integer.parseInt(scan.nextLine());
            break;
        } catch (NumberFormatException e) {
            System.out.println(&quot;에러 원인 = &quot; + e.getMessage());
            System.out.println(&quot;입력은 숫자로 하셔야 합니다! 번호를 다시 입력해 주세요.&quot;);
            continue;
        } catch (Exception e) {
            System.out.println(&quot;알 수 없는 입력이에요. 정확한 번호를 입력해 주세요.&quot;);
            continue;
        }
    }
    return selectNum;
}</code></pre>
<h3 id="--덱-만들기">- 덱 만들기</h3>
<p>게임이 시작됐으니, Deck을 만들어줘야 한다.</p>
<pre><code class="language-java">// 카드 덱 만들기
private void makeDeck() {
    for (int i = 0; i &lt; 30; i++) {
        deck.add(i % 10 + 1);
    }

//    블랙잭 확인용
//    for(int i=0;i&lt;2;i++) {
//        deck.add(1);
//        deck.add(10);
//        deck.add(1);
//    }

}</code></pre>
<p>나머지 연산을 이용해 1~10 까지의 카드를 덱에 넣어줬다.
아래의 for문은 BlackJack으로 결과가 나왔을 때, 이상이 있는지 확인하기 위해 만들어 줬다.</p>
<h3 id="--카드-두-장-배분">- 카드 두 장 배분</h3>
<pre><code class="language-java">private void drawTwoCard() {
    for (int i = 0; i &lt; 2; i++) {
        int rand1 = deck.remove(random.nextInt(deck.size()));
        int rand2 = deck.remove(random.nextInt(deck.size()));
        userSum += rand1;
        dealerSum += rand2;
        userCntCard.add(rand1);
        dealerCntCard.add(rand2);
    }
}</code></pre>
<p>덱이 만들어지면, 딜러와 플레이어에게 2장씩 카드를 지급한다.</p>
<pre><code class="language-java">...
drawTwoCard();

// 블랙잭일 경우
if ((userCntCard.contains(1) &amp;&amp; userCntCard.contains(10)) 
    || (dealerCntCard.contains(1) &amp;&amp; dealerCntCard.contains(10))) {
        printResult.checkBlackJack(userSum, dealerSum, userCntCard, dealerCntCard, money);
        break;
}
// 아닐 경우
printResult.currentCheck(userSum, dealerSum, userCntCard, dealerCntCard);
...</code></pre>
<p>이 때, 블랙잭일 경우는 결과를 출력하며 게임이 종료되고,
아닐 경우는 Hit or Stay부분으로 넘어간다.</p>
<h3 id="--hit-메서드">- Hit 메서드</h3>
<pre><code class="language-java">private void hit() {
    // 덱에서 랜덤하게 카드를 제거
    int rand1 = deck.remove(random.nextInt(deck.size()));
    // 그 값을 보유 카드에 추가
    userCntCard.add(rand1);
    userSum += rand1;

    // 딜러도 같이 hit를 하게 되는데 카드 합이 17미만일 경우만 hit를 한다.
    if (dealerSum &lt; DEALER_MIN_NUM) {
        int rand2 = deck.remove(random.nextInt(deck.size()));
        dealerSum += rand2;
        dealerCntCard.add(rand2);
    }
}

// hit로 게임이 끝났을 경우 결과 출력 메서드 (딜러 혹은 자신이 Bust가 난 경우)
private void hitResult(PrintString printResult) {
    if (userSum &gt; BLACK_JACK_NUM) {
        printResult.userLose(userSum, dealerSum, userCntCard, dealerCntCard);
    } else if (dealerSum &gt; BLACK_JACK_NUM) {
        printResult.userWin(userSum, dealerSum, userCntCard, dealerCntCard, money);
    }
}</code></pre>
<h3 id="--stay-메서드">- Stay 메서드</h3>
<pre><code class="language-java">private void stay() {
    // 플레이어가 stay시 딜러의 카드가 17미만일 경우 그 이상이 될 때까지 카드를 뽑는다.
    while (dealerSum &lt; DEALER_MIN_NUM) {
        int randCard = deck.remove(random.nextInt(deck.size()));
        dealerCntCard.add(randCard);
        dealerSum += randCard;
    }
}

// stay시 결과 출력 메서드
private void stayResult(PrintString printResult) {
    if (dealerSum &gt; BLACK_JACK_NUM) {
        printResult.userWin(userSum, dealerSum, userCntCard, dealerCntCard, money);
    } else if (userSum &gt; dealerSum) {
        printResult.userWin(userSum, dealerSum, userCntCard, dealerCntCard, money);
    } else if (userSum == dealerSum) {
        printResult.userDraw(userSum, dealerSum, userCntCard, dealerCntCard, money);
    } else if (userSum &lt; dealerSum) {
        printResult.userLose(userSum, dealerSum, userCntCard, dealerCntCard);
    }
}</code></pre>
<hr>
<h2 id="🎉-프로그램-시연">🎉 프로그램 시연</h2>
<h3 id="승리">승리</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/e48b1116-145d-4937-8bf0-9cac5e814a3b/image.gif" alt=""></p>
<h3 id="패배">패배</h3>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/a6bd49ec-e301-41d8-84ed-ca966a6a2f8d/image.gif" alt=""></p>
<h3 id="블랙-잭-결과">블랙 잭 결과</h3>
<h4 id="승리-1">승리</h4>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/0b741b35-98a6-496f-8b9f-6cdece26ac28/image.png" alt=""></p>
<h4 id="패배-1">패배</h4>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/cbc4ad75-ed9b-4296-96ea-6b16dce9a511/image.png" alt=""></p>
<h4 id="무승부">무승부</h4>
<p><img src="https://velog.velcdn.com/images/mandarine_punch/post/0569715f-adb5-4b6a-ac06-7ea9f070c58e/image.png" alt=""></p>
<p>처음에는 출력 콘솔창을 좀 예쁘게 하고 싶어서 출력문을 이것 저것 만져보았다.
하지만, Simple is best라고 ㅋㅋ 카드 카운팅이나 다른 조건 확인에 있어 그냥 텍스트만 보여주는게 깔끔한 것 같아 출력 문구 라인만 조금 맞춰줬다.</p>
<p>나름 재밌게 만들어져서 뿌듯하다.😆</p>
<blockquote>
<p><a href="https://github.com/MandarinePunch/blackjack/tree/master/blackjack">블랙 잭 코드 - GitHub</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[My toy] 움직이는 공 2 - JavaScript]]></title>
            <link>https://velog.io/@mandarine_punch/My-toy-%EC%9B%80%EC%A7%81%EC%9D%B4%EB%8A%94-%EA%B3%B5-2-JavaScript</link>
            <guid>https://velog.io/@mandarine_punch/My-toy-%EC%9B%80%EC%A7%81%EC%9D%B4%EB%8A%94-%EA%B3%B5-2-JavaScript</guid>
            <pubDate>Tue, 22 Mar 2022 14:20:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://mandarinepunch.github.io/BounceBall/">공 튕기기 (공도 잡을 수 있어요!)</a></p>
</blockquote>
<hr>
<h2 id="🔨-작업-코드">🔨 작업 코드</h2>
<h3 id="--블록-그리기">- 블록 그리기</h3>
<p>전 포스팅에 이어서 블록을 만들어보려 한다.
canvas를 사용하면서 느끼는건데, 이 녀석.. 정말 좌표뿐이다. ㅋㅋ
블록을 만들어주기 위해 좌표를 잡아준다.</p>
<pre><code class="language-javascript">export class Block {
  // 블록의 x, y 좌표와 길이와 높이를 받는다.
  constructor(x, y, width, height) {
    this.width = width;
    this.height = height;
    this.x = x;
    this.y = y;
  }

  draw(ctx) {
    ctx.fillStyle = &quot;tomato&quot;;
    ctx.beginPath();
    // 좌표를 받아 사각형을 만들어준다
    ctx.rect(this.x, this.y, this.width, this.height);
    ctx.fill();
  }
}</code></pre>
<pre><code class="language-javascript">class App {
  constructor() {

    ...

    this.ball = new Ball(this.stageWidth, this.stageHeight, 60, 10);
    // 임의로 블록의 크기를 지정
    this.block = new Block(200, 300, 400, 30);

    window.requestAnimationFrame(this.animate.bind(this));
  }

  ...

  animate() {
    window.requestAnimationFrame(this.animate.bind(this));
    this.ctx.clearRect(0, 0, this.stageWidth, this.stageHeight);
    // 블록 그리기
    this.block.draw(this.ctx);
    this.ball.draw(this.ctx, this.stageWidth, this.stageHeight);
  }
}</code></pre>
<p><img src="https://images.velog.io/images/mandarine_punch/post/eb045640-f74b-408b-b46a-48ab69e0058a/%EB%B8%94%EB%A1%9D1.PNG" alt=""></p>
<p>그럼 이렇게 블록이 하나 생긴다. 이렇게 두면 좀 밋밋하니까 블록에 moveTo, lineTo로 좌표를 이어서 입체처럼 보이게 만들어주자.</p>
<p><strong>그러면..</strong></p>
<p><img src="https://images.velog.io/images/mandarine_punch/post/9277e6d1-2d83-4290-9a9f-7712a75d1e1c/%EB%B8%94%EB%A1%9D2.PNG" alt=""></p>
<p>그럴싸한 블록이 만들어졌다! 하지만 아직 블록에 튕겨나가지 않으므로, 창에 튕기게 만든 것과 유사하게 블록도 함수를 만들어준다.</p>
<h3 id="--블록-형상화">- 블록 형상화</h3>
<pre><code class="language-javascript">class App {
  ...

  animate() {
    window.requestAnimationFrame(this.animate.bind(this));
    this.ctx.clearRect(0, 0, this.stageWidth, this.stageHeight);
    this.block.draw(this.ctx);
    // draw에 블록을 추가한 후
    this.ball.draw(this.ctx, this.stageWidth, this.stageHeight, this.block);
  }
}</code></pre>
<pre><code class="language-javascript">export class Ball {
  ...

  draw(ctx, stageWidth, stageHeight, block) {
    this.x += this.vx;
    this.y += this.vy;

    this.bounceWindow(stageWidth, stageHeight);
    // 블록 함수 추가
    this.bounceBlock(block);

    ...
  }
  ...

  bounceBlock(block) {
    const minX = block.x - this.radius;
    const maxX = block.maxX + this.radius;
    const minY = block.y - this.radius;
    const maxY = block.maxY + this.radius;
    // 블록 영역 정의
    if (this.x &gt; minX &amp;&amp; this.x &lt; maxX &amp;&amp; this.y &gt; minY &amp;&amp; this.y &lt; maxY) {
      // 블록의 x, y 중 어느 쪽에 닿았는지 판별
      const x1 = Math.abs(minX - this.x);
      const x2 = Math.abs(maxX - this.x);
      const y1 = Math.abs(minY - this.y);
      const y2 = Math.abs(maxY - this.y);
      const min1 = Math.min(x1, x2);
      const min2 = Math.min(y1, y2);
      const min = Math.min(min1, min2);

      if (min === min1) {
        this.vx *= -1;
        this.x += this.vx;
      } else if (min === min2) {
        this.vy *= -1;
        this.y += this.vy;
      }
    }
  }
}</code></pre>
<p><strong>그러면..</strong></p>
<p><img src="https://images.velog.io/images/mandarine_punch/post/aa5d7b55-0dd0-4032-b35c-ba258842e56b/%EB%B8%94%EB%A1%9D%EC%97%90%20%ED%8A%80%EB%8A%94%20%EA%B3%B5.gif" alt=""></p>
<p>블록에 정상적으로 튀는 공의 모습을 볼 수 있다.
그런데, 여기서 문제가 하나 발생한다.</p>
<h3 id="--버그-발견-🔍">- 버그 발견 🔍</h3>
<p>블록을 불가침 영역으로 지정했기 때문에, 처음 생성된 공이 블록 안에서 나타났을 경우,
공이 블록 안에 갇히는 현상이 나타난다.</p>
<p><img src="https://images.velog.io/images/mandarine_punch/post/d8a2d3d6-bc85-4b45-b35d-3ee52eb82b53/%EB%B8%94%EB%A1%9D%EC%97%90%20%EA%B0%87%ED%9E%8C%20%EA%B3%B5.gif" alt=""></p>
<p>이를 해결하기 위해, 조건문을 하나 지정해줬다.</p>
<h3 id="--버그-수정-✅">- 버그 수정 ✅</h3>
<pre><code class="language-javascript">class App {
  constructor() {
    ...
    this.block = new Block(200, 300, 400, 30);
    // 블록을 ball 안에 넣어준다.(블록의 좌표를 얻어야 하기 때문)
    this.ball = new Ball(this.stageWidth, this.stageHeight, 50, 5, this.block);

    window.requestAnimationFrame(this.animate.bind(this));
  }</code></pre>
<pre><code class="language-javascript">export class Ball {
  constructor(stageWidth, stageHeight, radius, speed, block) {
    ...

    const diameter = this.radius * 2;
    this.checkX = this.radius + Math.random() * (stageWidth - diameter);
    this.checkY = this.radius + Math.random() * (stageHeight - diameter);

    // 생성된 위치가 블록 안일 경우를 체크
    this.checkXYWithBlock =
      block.x - this.radius &lt; this.checkX &amp;&amp;
      this.checkX &lt; block.maxX + this.radius &amp;&amp;
      block.y - this.radius &lt; this.checkY &amp;&amp;
      this.checkY &lt; block.maxY + this.radius;

    // checkXYWithBlock이 참이면 블록 안에 공이 만들어진 것이므로
    // 갇히는 것을 막기 위해 좌측 상단에 공이 나오게 지정
    this.x = this.checkXYWithBlock ? this.radius : this.checkX;
    this.y = this.checkXYWithBlock ? this.radius : this.checkY;
  }
  ...
}</code></pre>
<p>이러면, 문제 없이 정상 작동하는 모습을 볼 수 있다!</p>
<h3 id="--추가-기능-구현">- 추가 기능 구현</h3>
<p>여기에서 끝내긴 좀 아쉬워서 <strong>공을 마음대로 움직이게 할 수는 없을까?</strong> 하는 생각에 마우스를 이용한 event를 추가로 만들어봤다. (drag &amp; drop)</p>
<pre><code class="language-javascript">export class Ball {
  constructor(stageWidth, stageHeight, radius, speed, block) {
    ...
    // 추가할 이벤트 함수를 변수로 선언 (그 이유는 뒤에서 말하겠다.)
    this.onMouseDown = this.mousedown.bind(this);
    this.onMouseUp = this.mouseup.bind(this);
    this.onMouseMove = this.mousemove.bind(this);
  }

  draw(ctx, stageWidth, stageHeight, block) {
    // mousedown 이벤트를 추가
    window.addEventListener(&quot;mousedown&quot;, this.onMouseDown);
    ...
  }

  setDirection() {
    // 현재 공의 방향을 변수에 저장
    this.vxDirection = this.vx &lt; 0 ? -1 : 1;
    this.vyDirection = this.vy &lt; 0 ? -1 : 1;
  }

  mousedown(event) {
    // 7. 다시 mousedown이 발생하면 맨 뒤에 발생했던 mouseup제거 (그 이유는 뒤에서 말하겠다.)
    window.removeEventListener(&quot;mouseup&quot;, this.onMouseUp);
    this.offsetX = event.clientX - this.x;
    this.offsetY = event.clientY - this.y;

    // 1. mousedown이 발생될 때 현재 공의 방향을 불러온다.
    this.setDirection();

    // 2. 만약 공 안에서 mousedown이 발생하면 mousemove를 이벤트를 실행
    if (
      Math.abs(this.offsetX) &lt;= this.radius &amp;&amp;
      Math.abs(this.offsetY) &lt;= this.radius
    ) {
      window.addEventListener(&quot;mousemove&quot;, this.onMouseMove);
    }
  }

  mousemove(event) {
    // 3. mousemove 이벤트 실행시 공이 멈추고 공이 마우스를 따라 이동
    this.x = event.clientX - this.offsetX;
    this.y = event.clientY - this.offsetY;

    this.vx = 0;
    this.vy = 0;

    // 4. 마우스를 떼면 mouseup 이벤트 실행
    window.addEventListener(&quot;mouseup&quot;, this.onMouseUp);
  }

  mouseup() {
    // 5. 공을 mousedown으로 집었을 때의 방향을 불러와 공이 다시 움직이도록 구현
    this.vx = this.speed;
    this.vy = this.speed;

    if (this.vxDirection &lt; 0) {
      this.vx *= -1;
    }
    if (this.vyDirection &lt; 0) {
      this.vy *= -1;
    }

    // 6. mousemove, mousedown 이벤트 제거
    window.removeEventListener(&quot;mousemove&quot;, this.onMouseMove);
    window.removeEventListener(&quot;mousedown&quot;, this.onMouseDown);
  }
}</code></pre>
<hr>
<h2 id="🎉-그-결과">🎉 그 결과</h2>
<p><img src="https://images.velog.io/images/mandarine_punch/post/42afc6f8-a78b-4364-86a3-f8066ec6a557/%EA%B3%B5%20%EB%A7%88%EB%AC%B4%EB%A6%AC.gif" alt=""></p>
<p>내 마음대로 조종할 수 있는 공이 완성됐다! (짝짝짝)</p>
<hr>
<h2 id="🩹-힘들었던-점">🩹 힘들었던 점</h2>
<p>class에서 event를 추가 및 제거함에 있어 뜻대로 되지 않는 동작이 잦았다.
특히, addEventListener(&quot;mouseup&quot;, <strong>this.function</strong>) 요 함수 부분이 <strong>this.function.bind(this)</strong> 처럼 bind로 감싸주지 않으면 동작 자체를 안했다.
그래서 bind로 감싸고 실행해봤더니 이번엔 removeEventListener가 동작을 안했다. 
(지금 생각해도 답답함 ㅋㅋ)
원리를 알고자 많이 찾아봤지만, 크게 이해가 되지 않았다.
그러다 bind에 관한 좋은 글 하나를 발견했다. -&gt; <a href="https://velog.io/@suld2495/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A6%AC%EC%8A%A4%EB%84%88%EC%99%80-bind">bind 알아보기</a></p>
<pre><code class="language-javascript">const object = {
    name: &#39;object&#39;,
    getName() {
        return this.name;
    }
}

const getName = object.getName;
const bindGetName = object.getName.bind(object);

console.log(getName === bindGetName); // false</code></pre>
<p>링크에 있는 글을 보면 위의 코드가 같지 않다는 것을 알 수 있다.</p>
<pre><code class="language-javascript"> class TryEvent {
    ...
    add() {
        this.element.addEventListener(&#39;click&#39;, this.click.bind(this)); 
    }

    remove() {
        this.element.removeEventListener(&#39;click&#39;, this.click);
        this.element.removeEventListener(&#39;click&#39;, this.click.bind(this));
    }
}</code></pre>
<p>따라서 이 3개의 함수도 다 다른 값을 가지고 있다는 것이다.
그래서 removeEventListener가 동작하지 않았다. 없는 event를 제거하려고 했으니 말이다.</p>
<p>이를 해결해 주기 위해 함수를 <strong>this.function.bind(this)</strong> 로 선언하고 변수를 만들어 이 값을 할당해줬다.</p>
<ul>
<li><strong>this.onMouseUp = this.mouseup.bind(this)</strong></li>
</ul>
<p>이러면 어디에서 event가 만들어져도 직접 함수 호출이 아닌 변수를 호출해 줌으로써 같은 event를 추가 및 제거할 수 있을 것이다. (지금 내 이해도로는 이게 최선이다 ㅠㅠ)
이렇게 가설을 세우고 실행하니 다행히도 event들이 정상적으로 작동했다!!!</p>
<p><strong>그런데.. 또 문제가 하나 생겼다.</strong>
mouseup 이벤트에서 공의 방향을 기억하고 있던 탓인지,
공이 아닌 빈 화면에 마우스를 눌렀다 뗐는데 갑자기 공의 방향이 바뀌는 것이었다. ㅋㅋ
이를 없애주기 위해 여러가지 시도를 해봤지만... 영 마음에 들지 않아,
그냥 mousedown 이벤트가 다시 발생할 때 기존에 있던 mouseup 이벤트를 없애줬다.</p>
<p>원하는 기능 구현이 순탄치는 않았지만, 그래도 얻는게 있어서 뿌듯하다 😄</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[My toy] 움직이는 공 1 - JavaScript]]></title>
            <link>https://velog.io/@mandarine_punch/My-toy-%EC%9B%80%EC%A7%81%EC%9D%B4%EB%8A%94-%EA%B3%B5-1-JavaScript</link>
            <guid>https://velog.io/@mandarine_punch/My-toy-%EC%9B%80%EC%A7%81%EC%9D%B4%EB%8A%94-%EA%B3%B5-1-JavaScript</guid>
            <pubDate>Tue, 22 Mar 2022 11:34:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://mandarinepunch.github.io/BounceBall/">공 튕기기 (공도 잡을 수 있어요!)</a></p>
</blockquote>
<hr>
<h2 id="✍-만들게-된-계기">✍ 만들게 된 계기</h2>
<p>Java공부를 하던 중, 가볍게 재밌는 프로젝트를 만들고 싶어 <a href="https://www.youtube.com/c/cmiscm">Interactive Developer</a> 김종민님의 유튜브를 찾아봤다. 사실 canvas도 주먹구구식으로 찾아본 것이 전부라 조금 더 알고싶은 마음에서도 있었다.
그렇게 첫 영상을 시청했는데.. <strong>프로그래밍 방식이 굉장히 좋아보였다!</strong>
현재 객체지향 언어를 공부 중이지만, 실상 프로그래밍 방식은 객체지향과는 거리가 멀게 구현하고 있었는데, 
김종민님은 스크립트 언어인 JS를 객체지향 방식으로 구현하고 있었다.
굉장히 어려워 보였는데, 코드가 확실히 깔끔해서 그 방식을 따라해보기로 했다.</p>
<hr>
<h2 id="🔨-작업-코드">🔨 작업 코드</h2>
<p>우선 캔버스를 미리 세팅해준다.</p>
<pre><code class="language-javascript">class App {
  constructor() {
    // canvas를 생성해주고
    this.canvas = document.createElement(&quot;canvas&quot;);
    // body에 추가한다.
    document.body.appendChild(this.canvas);

    this.ctx = this.canvas.getContext(&quot;2d&quot;);
    // 디바이스에 따라 선명도를 올려주기 위해 사용
    this.pixelRatio = window.devicePixelRatio &gt; 1 ? 2 : 1;

    // 창 사이즈가 변할 때마다 캔버스를 변화시켜줘야 하기에 선언
    window.addEventListener(&quot;resize&quot;, this.resize.bind(this), false);
    this.resize();

    // 움직이는 공을 위한 애니메이션 함수
    window.requestAnimationFrame(this.animate.bind(this));
  }

  resize() {
    // resize때마다 canvas의 width, height를 창 사이즈로 만들어 주기 위함.
    this.stageWidth = document.body.clientWidth;
    this.stageHeight = document.body.clientHeight;

    this.canvas.width = this.stageWidth * this.pixelRatio;
    this.canvas.height = this.stageHeight * this.pixelRatio;
    // 선명도를 좋게 해주기 위함
    this.ctx.scale(this.pixelRatio, this.pixelRatio);
  }

  animate() {
    window.requestAnimationFrame(this.animate.bind(this));
  }
}
// 캔버스 실행
window.onload = () =&gt; {
  new App();
};</code></pre>
<p>김종민님의 영상을 보면 거의 이런 식으로 캔버스를 미리 세팅한다.
처음에는 잘 이해가 안갔는데, 몇 번 해보니 이 방식이 편하다는 것을 알았다.</p>
<h3 id="--공-만들기">- 공 만들기</h3>
<p>캔버스는 만들어졌으니 공을 만들어주도록 하자
우선, 공이 나타나는 위치를 먼저 지정해준다.</p>
<pre><code class="language-javascript">
export class Ball {
  // 처음 공의 위치를 화면 내에 랜덤하게 줄 예정이기에, 현재화면의 width와 height를 가져온다.
  constructor(stageWidth, stageHeight, radius, speed) {
    this.radius = radius;
    // 공이 움직이는 속도
    this.vx = speed;
    this.vy = speed;

    // 우선 공의 지름을 잡는다.
    const diameter = this.radius * 2;
    // 공이 화면 밖에 생성되면 안되기 때문에 원의 중앙(x, y)을 잡아준다.
    this.x = this.radius + Math.random() * (stageWidth - diameter);
    this.y = this.radius + Math.random() * (stageHeight - diameter);
  }
}</code></pre>
<p>공의 위치를 잡았으니 이제 공을 만들어보자.</p>
<pre><code class="language-javascript">import { Ball } from &quot;./ball.js&quot;;

class App {
  constructor() {
    ...
    // 공을 만들어 준다.
    this.ball = new Ball(this.stageWidth, this.stageHeight, 60, 10);
    window.requestAnimationFrame(this.animate.bind(this));
  }
  ...
}</code></pre>
<pre><code class="language-javascript">export class Ball {
  constructor(stageWidth, stageHeight, radius, speed) {
    ...
  }
  // 공을 그릴 함수
  draw(ctx) {
    ctx.fillStyle = &quot;yellow&quot;;
    ctx.beginPath();
    // 현재 중심 (x, y)에서 원을 만든다.
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
    ctx.fill();
  }
}</code></pre>
<p><img src="https://images.velog.io/images/mandarine_punch/post/0ba295d8-700a-4e32-b38a-6c90db0f27ac/%EC%B2%AB%20%EA%B3%B5.PNG" alt=""></p>
<p>짠! 노란 공이 그려졌다. 이제 애니메이션을 이용해서 공을 움직여 보도록 하자.</p>
<h3 id="--공-움직이기">- 공 움직이기</h3>
<pre><code class="language-javascript">export class Ball {
  constructor(stageWidth, stageHeight, radius, speed) {
    ...
  }

  draw(ctx, stageWidth, stageHeight) {
    // 지속적으로 값이 증가함으로써 공이 움직이는 것처럼 보일 예정
    this.x += this.vx;
    this.y += this.vy;
    // 공이 화면에 닿으면 튀게끔 함수를 만듦
    this.bounceWindow(stageWidth, stageHeight);

    ctx.fillStyle = &quot;yellow&quot;;
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
    ctx.fill();
  }

  bounceWindow(stageWidth, stageHeight) {
    const minX = this.radius;
    const maxX = stageWidth - this.radius;
    const minY = this.radius;
    const maxY = stageHeight - this.radius;
    // 창 끝에 닿으면
    if (this.x &lt;= minX || this.x &gt;= maxX) {
      // 증가 값을 음수로 만들어 반대로 이동하게 한다.
      this.vx *= -1;
      this.x += this.vx;
    }
    if (this.y &lt;= minY || this.y &gt;= maxY) {
      this.vy *= -1;
      this.y += this.vy;
    }
  }
}</code></pre>
<pre><code class="language-javascript">import { Ball } from &quot;./ball.js&quot;;

class App {
  constructor() {
    ...
    // 공을 만들어 준다.
    this.ball = new Ball(this.stageWidth, this.stageHeight, 60, 10);

    window.requestAnimationFrame(this.animate.bind(this));
  }

  ...

  animate() {
    window.requestAnimationFrame(this.animate.bind(this));
    // 만들어진 공을 그린다.
    this.ball.draw(this.ctx, this.stageWidth, this.stageHeight);
  }
}</code></pre>
<p><img src="https://images.velog.io/images/mandarine_punch/post/7acf0a9d-7d34-49af-9286-c979f7e1edc3/%EA%B3%B5%20%EA%B7%B8%EB%A6%AC%EA%B8%B0.gif" alt=""></p>
<h3 id="--버그-발견-🔍">- 버그 발견 🔍</h3>
<p>공이 튀긴하는데.. 남겨진 잔상이 없어지지 않아서 이런 현상이 발생하는 것 같다.
animate함수에 애니메이션을 실행할때마다 canvas를 clear하도록 수정해준다.</p>
<h3 id="--버그-수정-✅">- 버그 수정 ✅</h3>
<pre><code class="language-javascript">  animate() {
    window.requestAnimationFrame(this.animate.bind(this));
    // 애니메이션 함수 호출시 캔버스 clear
    this.ctx.clearRect(0, 0, this.stageWidth, this.stageHeight);

    this.ball.draw(this.ctx, this.stageWidth, this.stageHeight);
  }</code></pre>
<p>그 결과..</p>
<p><img src="https://images.velog.io/images/mandarine_punch/post/6824ff0f-d11b-4e8c-986f-43e7541dbcdf/%ED%8A%80%EB%8A%94%20%EA%B3%B5.gif" alt=""></p>
<p>공이 튀는 듯한 모션으로 바뀌었다! 이제 가운데 블록을 만들어 블록에 닿아도 튕겨 나가게 만들어 줘야하는데 글이 많이 길어질 것 같아 다음편에 이어서 쓰겠다. 😀</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[My Toy] 혼자서 즐겨보는 벚꽃 축제 - JavaScript]]></title>
            <link>https://velog.io/@mandarine_punch/My-Toy-%ED%98%BC%EC%9E%90%EC%84%9C-%EC%A6%90%EA%B2%A8%EB%B3%B4%EB%8A%94-%EB%B2%9A%EA%BD%83-%EC%B6%95%EC%A0%9C-JavaScript</link>
            <guid>https://velog.io/@mandarine_punch/My-Toy-%ED%98%BC%EC%9E%90%EC%84%9C-%EC%A6%90%EA%B2%A8%EB%B3%B4%EB%8A%94-%EB%B2%9A%EA%BD%83-%EC%B6%95%EC%A0%9C-JavaScript</guid>
            <pubDate>Mon, 14 Mar 2022 13:29:35 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://mandarinepunch.github.io/drawtree/">벚나무 페이지 링크</a></p>
</blockquote>
<h3 id="✍-만들게-된-계기">✍ 만들게 된 계기</h3>
<p>여느 때와 다를 것 없이 Java로 코딩테스트를 연습 중이었다.
그 후 블로그 작성을 위해 velog를 켰는데 우연히 보게 된 <a href="https://velog.io/@heekang/Vanilla-JS-%EC%9E%90%EB%9D%BC%EB%82%98%EB%8A%94-%EB%82%98%EB%AC%B4-%EB%A7%8C%EB%93%A4%EA%B8%B0-1">신희강님</a>의 작업물들 중 하나를 보고 적지 않은 충격을 받았다.
<a href="https://www.youtube.com/c/cmiscm">Interactive Developer</a> 김종민님의 영상을 참고해 만든 Tree Artwork이었다.
정말... 정말 실사 같았다. 그리고 무엇보다도 <strong>작품이 아름다웠다.</strong>
어떻게 JavaScript로 이런 작업물을 만들 수 있는건지 너무 감탄했다.
이걸 본 이상  안 만들어 볼 수는 없어 코드도 참고하고, canvas에 대해 이것저것 찾아보며 나만의 나무를 만들어봤다.</p>
<hr>
<h3 id="🔨-작업-코드">🔨 작업 코드</h3>
<p>먼저 canvas를 html코드에 넣고 작업을 시작했다.</p>
<pre><code class="language-html">  &lt;body&gt;
    &lt;canvas&gt;&lt;/canvas&gt;
    &lt;script src=&quot;app.js&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;</code></pre>
<p>그 후, 캔버스를 js로 가져오고 캔버스의 크기를 현재 내가 보고있는 창의 크기로 맞춰줬다.</p>
<pre><code class="language-javascript">const canvas = document.querySelector(&quot;canvas&quot;);
const ctx = canvas.getContext(&quot;2d&quot;);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;</code></pre>
<p>오랜만에 JS를 만지는 거라 뭐가 안될 때 마다 계속 console창을 들여다 봤다. ㅋㅋ (게다가 캔버스는 거의 초면 수준인지라....)
도화지는 만들어졌으니 이제 나무를 그려야 한다.
그리기 위해서는 좌표가 필요한데 그 식은 삼각함수를 이용하여 비교적 간단히 구현할 수 있었다.</p>
<pre><code class="language-javascript">const draw = (startX, startY, startDepth, angle, branchWidth) =&gt; {

  ctx.beginPath();

  let len = 100;
  const endX =
    // 빗변 길이와 cosθ을 곱해 x좌표를
    startX + Math.cos((angle / 180) * Math.PI) * len;
  const endY =
    // 빗변 길이에 sinθ를 곱해 y좌표를 구한다.
    startY + Math.sin((angle / 180) * Math.PI) * len;

  // moveTo로 위치를 잡아주고
  ctx.moveTo(startX, startY);
  // lineTo의 좌표까지 직선을 잇는다.
  ctx.lineTo(endX, endY);

  ctx.lineWidth = branchWidth;
  ctx.stroke();

};
// 첫 각도를 -90도로 맞춰야 이 친구가 화면 위로 우뚝 선다.
draw(canvas.width/2 ,canvas.height, 0, -90, 15);</code></pre>
<p><img src="https://images.velog.io/images/mandarine_punch/post/f284acbc-c124-4305-aced-23892cd86b25/%EB%B0%91%EB%91%A5.PNG" alt=""></p>
<p>짜잔! 귀여운 나무 밑둥이 만들어졌다! 이제 가지를 뻗어나가는 일만 남았다.
열심히 찾고 생각한 결과 각도를 좌우로 틀고 재귀 함수를 호출해주는 것이 가장 간단히 구현할 수 있는 방법이었다. (DFS 공부가 도움이 됐다!)</p>
<pre><code class="language-javascript">const depth = 4;

const draw = (startX, startY, startDepth, angle, branchWidth) =&gt; {
  // 뻗는 가지 갯수를 depth로 조정한다.
  if (startDepth === depth) {
      return;
  }

  ctx.beginPath();

  let len = 100;
  const endX =
    startX + Math.cos((angle / 180) * Math.PI) * len;
  const endY =
    startY + Math.sin((angle / 180) * Math.PI) * len;

  ctx.moveTo(startX, startY);
  ctx.lineTo(endX, endY);

  ctx.lineWidth = branchWidth;
  ctx.stroke();
  // 재귀 함수 호출
  draw(endX, endY, startDepth + 1, angle - 30, branchWidth);
  draw(endX, endY, startDepth + 1, angle + 30, branchWidth);

};

draw(canvas.width / 2 ,canvas.height, 0, -90, 15);</code></pre>
<p><img src="https://images.velog.io/images/mandarine_punch/post/58c9e817-746d-408e-92c2-e9766212f451/%EA%B0%88%EB%9D%BC%EC%A7%84%EB%82%98%EB%AC%B4.PNG" alt=""></p>
<p>제법 예쁘게 갈라진 모습이 매력적이다.
이제 갈라지는 횟수를 담당하는 depth를 늘려주고 30°로 정해놓은 각도도 랜덤하게 바꾼다.
branchWidth도 가지가 뻗어갈수록 얇아지게하면 더욱 그럴싸한 나무가 된다.</p>
<pre><code class="language-javascript">const depth = 11;

const draw = (startX, startY, startDepth, angle, branchWidth) =&gt; {

  ...
  // 첫 밑둥을 만들 때는 어느정도 고정길이를 주고, 그 이후에 뻗어나오는 가지는 랜덤하게 길이를 주었다.
  let len = startDepth === 0 ? random(10, 14) : random(0, 10);
  const endX =
    // 첫 밑둥을 최대 길이로 하고 가지가 늘어날수록 길이를 작아지게 하기위해 len뒤에 식을 넣어줬다.
    startX + Math.cos((angle / 180) * Math.PI) * len * (depth - startDepth);
  const endY =
    startY + Math.sin((angle / 180) * Math.PI) * len * (depth - startDepth);

  ...
  // 재귀를 할때마다 branchWidth를 0.8배씩 조정하여 굵기를 줄였다.
  draw(endX, endY, startDepth + 1, angle - random(15, 25), branchWidth * 0.8);
  draw(endX, endY, startDepth + 1, angle + random(15, 25), branchWidth * 0.8);

};
// Math.random() MDN을 보면 나오는 사잇값 구하는 방법에 1 이상이 나오도록 뒤에 1을 더해줬다.
const random = (min, max) =&gt; {
  return min + Math.floor(Math.random() * (max - min) + 1);
};

draw(canvas.width / 2 ,canvas.height, 0, -90, 15);</code></pre>
<p><img src="https://images.velog.io/images/mandarine_punch/post/d134231c-9d12-476d-b363-f71a88fe81c2/%EB%82%98%EB%AC%B4.PNG" alt=""></p>
<p>감동... 너무 예쁜 나무가 나왔다.
여기서 더 뭘해줄까 하다가, 이제 날도 따뜻해지는 것 같고 봄이 오는 느낌이 슬슬 들기에 코로나를 의식한 조금 이른 벚꽃 축제를 열어보기로 했다. ㅋㅋ</p>
<p>일단 나무가 많아야 하므로 나무 호출식(draw)의 x좌표를 clientX를 이용하여, 마우스 클릭시 그 곳에서 나무가 나오게 수정하였다.</p>
<pre><code class="language-javascript">const handleClick = (event) =&gt; {
  const { clientX } = event;
  draw(clientX, canvas.height, 0, -90, 15);
};

window.addEventListener(&quot;click&quot;, handleClick);</code></pre>
<p>메인 주제인 벚꽃을 표현해 줘야 하므로, 처음에는 분홍색 단색만 써서 출력해봤다.
예쁘긴한데 뭔가 밋밋한 느낌이 있어, 배열을 선언하여 조금씩 다른 분홍들을 몇가지 넣고
가지가 끝에 다다랐을 때 랜덤하게 배열에서 뽑아 색을 입혀주었다.</p>
<pre><code class="language-javascript">const draw = (startX, startY, startDepth, angle, branchWidth) =&gt; {
  ...
  // 나무 굵기가 일정 수치 이하로 떨어지면
  if (branchWidth &lt; 2) {
    // 꽃의 크기를 랜덤하게 부여하고
    branchWidth = random(3, 5);
    // 색도 랜덤하게 넣는다.
    ctx.strokeStyle = color[Math.floor(Math.random() * 4)];
  } else {
    ctx.strokeStyle = &quot;black&quot;;
  }
  ctx.lineWidth = branchWidth;
  ctx.stroke();

  draw(endX, endY, startDepth + 1, angle - random(15, 25), branchWidth * 0.8);
  draw(endX, endY, startDepth + 1, angle + random(15, 25), branchWidth * 0.8);
}</code></pre>
<p>마지막으로, 창이 늘고 줄때마다 나무 위치가 이상해지길래 그냥 창 사이즈가 바뀔때마다 캔버스를 지우고 바뀐 창 사이즈에 새로 나무를 만들게끔 resize 함수도 구현했다.</p>
<pre><code class="language-javascript">const handleResize = () =&gt; {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
};

window.addEventListener(&quot;resize&quot;, handleResize);</code></pre>
<h3 id="🎉-그-결과">🎉 그 결과...</h3>
<p><img src="https://images.velog.io/images/mandarine_punch/post/d8fe1396-92f0-4d26-8e96-55047a7a4e9a/%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%B2%9A%EA%BD%83%EB%82%98%EB%AC%B4.gif" alt=""></p>
<p>생각보다 너무 잘 나온것 같아 뿌듯하다.
여담으로 나무를 빽빽히 찍어내면 이런 장관도 볼수 있다.
<img src="https://images.velog.io/images/mandarine_punch/post/1c4c8706-8842-4b79-a6ff-3838cfb6d10c/%EC%BB%B4%ED%93%A8%ED%84%B0%EB%B2%9A%EA%BD%832.png" alt=""></p>
<p><strong>(얼핏 보면 사진같다. ㅎㅎ)</strong></p>
<hr>
<h3 id="📌-깨달은-점">📌 깨달은 점</h3>
<p>canvas라는 태그 자체가 굉장히 생소해서 이것저것 꽤 오래 찾아본 것 같다.</p>
<ul>
<li>canvas를 window사이즈로 맞추는 방법</li>
<li>moveTo로 시작 위치를 잡아야 한다는 것 <strong>&lt;- (이거 깨닫는게 너무 오래 걸렸음)</strong> </li>
<li>lineTo로 선을 이을 지점을 잡아야 한다는 것</li>
<li>stroke로 직접 선을 그려줘야 한다는 것</li>
</ul>
<p>stroke로 선을 그려줘야 한다는 것도 몰라서 moveTo, lineTo만 선언해 놓고 <strong>어..? 왜 선이 안나오지?</strong> 이러고 있었다. ㅋㅋ</p>
<p>stroke를 쓰고도 난관이 있었는데... 분명 초기 위치를 moveTo로 잘 잡아주었음에도 불구하고, 첫 밑둥을 만드는데 선이 중구난방으로 튀었었다. ㅋㅋ (moveTo(0, 0)으로 시작 좌표를 잡으면 화면 좌측 상단부터 선이 시작된다.)</p>
<p>재귀함수로 가지를 뻗어나가게 구현할 때도 문제가 있었는데,
코드가 뭔가 이상했는지 오른쪽 왼쪽으로 재귀를 2개를 써줬음에도 불구하고, 왼쪽 가지만 엄청 늘어났었다. <strong>(오른쪽 가지 어디갔냐고!)</strong>
결국 코드 선언 순서가 문제긴 했다.</p>
<p>정말 오랜만에 모르는 분야를 찾아가며 구현한 것 같다.
생소한 만큼 정보를 찾느라 힘들었지만, 이 프로젝트 전후로 검색 실력이 달라진건 확실하다. ㅋㅋ
이렇게 느낌이 오는 날 종종 다른 것도 만들어야겠다. <strong>(JS..최고야...)</strong>
<strong>너무 재밌었던 나만의 벚꽃 축제였다!</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[My Toy] 러시안 룰렛 - JAVA]]></title>
            <link>https://velog.io/@mandarine_punch/%ED%86%A0%EC%9D%B4-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%9F%AC%EC%8B%9C%EC%95%88-%EB%A3%B0%EB%A0%9B-JAVA</link>
            <guid>https://velog.io/@mandarine_punch/%ED%86%A0%EC%9D%B4-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%9F%AC%EC%8B%9C%EC%95%88-%EB%A3%B0%EB%A0%9B-JAVA</guid>
            <pubDate>Fri, 11 Mar 2022 09:08:58 GMT</pubDate>
            <description><![CDATA[<p>어제 학원 먼저 번 기수들의 프로젝트 발표를 봤는데, 보면서 느끼는 바가 많았다. 그래서 나도 코딩테스트만 죽어라 파기보다는 <strong>혼자 간단한 프로젝트를 만드는 게 낫겠다.</strong> 싶은 생각이 들었다.</p>
<p>자바라는 언어로 처음이니만큼 크게 어렵게는 하고 싶지 않아, 이번에 배운 메서드와 반복문을 토대로 게임을 하나 구현했다. ㅎㅎ</p>
<p>그리고 여담이지만.. Java에서는 C의 scanf처럼 값을 받아주려면 Scanner를 만들어야 한다는 것을 이 프로젝트를 만들면서 알았다 ㄷㄷ.</p>
<h3 id="게임-내용은-다음과-같다">게임 내용은 다음과 같다.</h3>
<ul>
<li>일반 러시안 룰렛과 규칙은 같다.</li>
<li>단순히 게임 오버만 되면 재미가 없을 것 같아, 도박에 성공할 때마다 돈을 지급하기로 했다. (물론 게임머니)</li>
<li>남은 기회를 count하고 탄약이 한 발 남을 때까지 죽지 않으면, 모든 상금을 획득한다.</li>
<li>물론 중간에 게임을 포기할 수도 있다. 그 경우 지금까지 딴 돈을 획득한다.</li>
</ul>
<h3 id="러시안-룰렛-게임-메서드">러시안 룰렛 게임 메서드</h3>
<pre><code class="language-java">// 입력값(num), 남은 기회(count), 리볼버 탄창(cylinder), scan을 매개변수로 지정했다.
public static void playRoulette(int num, int count, int[] cylinder, Scanner scan) {
    int i = 0;
    int money = 20;

    while(true) {
        if(count == 0) {
            System.out.println(&quot;대단한 배짱이군요! 당신은 살아남으셨습니다! 획득 머니 : &quot;+(double)money/10000+&quot;억원&quot;);
            break;
        }
        System.out.println(&quot;1.발사   2.여기서 그만&quot;);
        num = scan.nextInt();
        if (num == 1) {
            if(bullet == cylinder[i]) {
                System.out.println(&quot;빵! 사망하셨습니다.&quot;);
                break;
            } else {
                count--;
                money *= 5;
                if(money &gt;= 10000) {
                    System.out.println(&quot;찰칵... lucky! 운이 좋으시군요. 남은 기회 : &quot;+count+&quot;, 획득 머니 : &quot;+(double)money/10000+&quot;억원&quot;);
                }else {                        
                    System.out.println(&quot;찰칵... lucky! 운이 좋으시군요. 남은 기회 : &quot;+count+&quot;, 획득 머니 : &quot;+money+&quot;만원&quot;);
                }
                i++;
            }
        } else if (num == 2) {
            if(i == 0) {
                System.out.println(&quot;들어온 이상 한 발은 쏴야합니다..😈&quot;);
                continue;
            }else {                    
                System.out.println(&quot;잘 생각하셨습니다. 목숨은 소중하니까요. 획득 머니 : &quot;+money+&quot;만원&quot;);
                break;
            }
        } else {
            System.out.println(&quot;1 또는 2를 눌러주세요&quot;);
            continue;
        }
    }
}</code></pre>
<p>메모장에 먼저 짜야할 로직을 적어놓고 구현하면서 부족한 점을 고쳐나갔다. 정말 간단한 게임이지만, 테스트를 하면서 왠지 모를 중독성도 있는 것 같았다 ㅋㅋ.
매일은 아니더라도 종종 가벼운 프로젝트를 만드는 것도 괜찮은 것 같다.</p>
<p>처음에는 for문으로 감쌌었는데 증감값 i가 자꾸 걸리적거려서 while문으로 바꿔버렸다.</p>
<h3 id="전체-코드">전체 코드</h3>
<pre><code class="language-java">package russianroulette;

import java.util.Random;
import java.util.Scanner;

public class Roulette {
    static Random random = new Random();
    static int bullet;

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int[] cylinder = { 0, 1, 2, 3, 4, 5 };
        int count = cylinder.length-1;
        bullet = random.nextInt(cylinder.length);
        int num;

        System.out.println(&quot;        |\\___________________,&quot;);
        System.out.println(&quot;       |    ===== _______)___) ==========|||)&quot;);
        System.out.println(&quot;      |       ||----||&quot;);
        System.out.println(&quot;   __/___   ====/&quot;);
        System.out.println(&quot;  (O___)\\\\_(_/&quot;);
        System.out.println(&quot; (O___)&quot;);
        System.out.println(&quot;(O___)&quot;);
        System.out.println(&quot;러시안 룰렛에 오신 것을 환영합니다.&quot;);
        while (true) {
            System.out.println(&quot;1.게임 시작     2.게임 종료&quot;);
            num = scan.nextInt();
            if (num == 1) {
                System.out.println(&quot;★☆★☆★☆★게임을 시작하겠습니다★☆★☆★☆★&quot;);
                System.out.println(&quot;당신은 스스로 머리에 총을 겨냥하고 있습니다.&quot;);
                playRoulette(num, count, cylinder, scan);
                break;
            } else if (num == 2) {
                System.out.println(&quot;해보지도 않고.. 겁쟁이! 넌 못 나가!&quot;);
                continue;
            } else {
                System.out.println(&quot;1 또는 2를 눌러주세요&quot;);
                continue;
            }
        }
    }

    public static void playRoulette(int num, int count, int[] cylinder, Scanner scan) {
        int i = 0;
        int money = 20;

        while(true) {
            if(count == 0) {
                System.out.println(&quot;대단한 배짱이군요! 당신은 살아남으셨습니다! 획득 머니 : &quot;+(double)money/10000+&quot;억원&quot;);
                break;
            }
            System.out.println(&quot;1.발사   2.여기서 그만&quot;);
            num = scan.nextInt();
            if (num == 1) {
                if(bullet == cylinder[i]) {
                    System.out.println(&quot;빵! 사망하셨습니다.&quot;);
                    break;
                } else {
                    count--;
                    money *= 5;
                    if(money &gt;= 10000) {
                        System.out.println(&quot;찰칵... lucky! 운이 좋으시군요. 남은 기회 : &quot;+count+&quot;, 획득 머니 : &quot;+(double)money/10000+&quot;억원&quot;);
                    }else {                        
                        System.out.println(&quot;찰칵... lucky! 운이 좋으시군요. 남은 기회 : &quot;+count+&quot;, 획득 머니 : &quot;+money+&quot;만원&quot;);
                    }
                    i++;
                }
            } else if (num == 2) {
                if(i == 0) {
                    System.out.println(&quot;들어온 이상 한 발은 쏴야합니다..😈&quot;);
                    continue;
                }else {                    
                    System.out.println(&quot;잘 생각하셨습니다. 목숨은 소중하니까요. 획득 머니 : &quot;+money+&quot;만원&quot;);
                    break;
                }
            } else {
                System.out.println(&quot;1 또는 2를 눌러주세요&quot;);
                continue;
            }
        }
    }
}</code></pre>
<h3 id="시연-영상">시연 영상</h3>
<p><img src="https://images.velog.io/images/mandarine_punch/post/75459034-c161-47e0-b6a0-4d92bb61b144/%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%9F%AC%EC%8B%9C%EC%95%88%20%EB%A3%B0%EB%A0%9B.gif" alt=""></p>
<p>이 게임... 꽤 중독성 있다...😝</p>
<p>총 모양 출처 : <a href="https://asciiart.cc/ko/view/11582">아스키 아트</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 여행경로 - JAVA]]></title>
            <link>https://velog.io/@mandarine_punch/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%97%AC%ED%96%89%EA%B2%BD%EB%A1%9C-JAVA</link>
            <guid>https://velog.io/@mandarine_punch/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%97%AC%ED%96%89%EA%B2%BD%EB%A1%9C-JAVA</guid>
            <pubDate>Wed, 09 Mar 2022 14:48:02 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-링크">문제 링크</h3>
<blockquote>
<p><a href="https://programmers.co.kr/learn/courses/30/lessons/43164">https://programmers.co.kr/learn/courses/30/lessons/43164</a></p>
</blockquote>
<h3 id="문제-설명">문제 설명</h3>
<p><img src="https://images.velog.io/images/mandarine_punch/post/aac74a8b-5511-4c09-b32e-d6148e5be449/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%97%AC%ED%96%89%EA%B2%BD%EB%A1%9C.PNG" alt=""></p>
<hr>
<p><strong>DFS/BFS</strong> 공부 중 풀어본 문제다. 이론은 대충 알겠는데 막상 구현하려니 머리가 터질 것 같았다. ㅋㅋㅋ
<strong>DFS</strong>는 <strong>깊이 우선 탐색</strong>으로 <strong>함수 속에 함수... 그 속에 함수... 또 그 속에 함수...</strong> 이런 식으로 동작하게끔 재귀함수를 호출하여 구현하는 것이 대부분이다.
나중에 더 공부하다 이론과 구현이 잘 정립되면 포스팅 해봐야겠다.
이 문제는 DFS를 이용하라고 말해주고 있는 것 같아서 DFS로 풀었다.</p>
<p>문제를 보면 알다시피, 만약 내가 갈 다음 도시가 하나가 아니라 여러 곳으로 배열이 존재한다면, 알파벳 순서가 앞서는 경로를 return하라고 되어있다. 이것을 어떻게 할까 하다 저번에 자동으로 값을 정리해주는 아주 편리한 PriorityQueue가 퍼뜩 생각이 났다.</p>
<h3 id="통과-코드">통과 코드</h3>
<pre><code class="language-java">import java.util.*;

class Solution {
    // 들어온 문자열을 오름차순으로 정렬해주기 위해 PriorityQueue를 만들었다.
    public Queue&lt;String&gt; pq = new PriorityQueue&lt;&gt;();
    // 요건 dfs에서 지나간 배열을 다시 들르게 하지 않기 위한 플래그이다.
    public boolean[] visited;

    public String[] solution(String[][] tickets) {
        visited = new boolean[tickets.length];
        // 처음 출발은 무조건 &quot;ICN&quot;공항에서 출발한다기에 그냥 값으로 못박아버렸다.
        dfs(tickets, &quot;ICN&quot;, 0, &quot;ICN&quot;);
        // PriorityQueue에 들어온 값들 중 처음 값이 최적 경로이므로 첫 값을 배열로 넣었다.
        String[] answer = pq.peek().split(&quot;,&quot;);
        return answer;
    }

    public void dfs(String[][] tickets, String currentCity, int cnt, String path){
        // 5. count가 tickets.length와 같으면 모든 배열을 순회한 것이므로
        if(cnt == tickets.length){
            // 6. pq에 전체 여행 경로를 추가
            pq.add(path);
            return;
        }
        for(int i=0;i&lt;tickets.length;i++){
            // 1. 간 적이 없고 &amp;&amp; 현재 도시가 다음 여행 경로의 도착지라면
            if(!visited[i] &amp;&amp; currentCity.equals(tickets[i][0])){
                // 2. 갔다고 체크
                visited[i] = true;
                // 3. 그 다음 경로 탐색
                dfs(tickets, tickets[i][1], cnt+1, path +&quot;,&quot;+ tickets[i][1]);
                // 4. 모든 배열을 순회했으면, 플래그를 초기화
                visited[i] = false;
            }
        }
    }
}</code></pre>
<p>우선 순위 큐(PriorityQueue) 덕분에 따로 값 정렬을 안해도 돼서 좋았다. 그리고 여행 경로를 어떻게 넣을까하다 <strong>&#39;결국 알파벳 우선 순위만 비교하면 되는것 아닌가?&#39;</strong> 라는 생각이 들어 여행 경로를 하나의 문자열( ex - &quot;ICN,JFK,HND...&quot; )로 만들어 queue에 추가했다.</p>
<h3 id="최종-코드">최종 코드</h3>
<pre><code class="language-java">import java.util.*;

class Solution {
    public Queue&lt;String&gt; pq = new PriorityQueue&lt;&gt;();
    public boolean[] visited;

    public String[] solution(String[][] tickets) {
        visited = new boolean[tickets.length];
        dfs(tickets, &quot;ICN&quot;, 0, &quot;ICN&quot;);
        String[] answer = pq.peek().split(&quot;,&quot;);
        return answer;
    }

    public void dfs(String[][] tickets, String currentCity, int cnt, String path){
        if(cnt == tickets.length){
            pq.add(path);
            return;
        }
        for(int i=0;i&lt;tickets.length;i++){
            if(!visited[i] &amp;&amp; currentCity.equals(tickets[i][0])){
                visited[i] = true;
                dfs(tickets, tickets[i][1], cnt+1, path +&quot;,&quot;+ tickets[i][1]);
                visited[i] = false;
            }
        }
    }
}</code></pre>
<p>여러 사람들의 코드를 볼 때, 주석이 달려있으면 한 줄, 한 줄 이해하기는 좋지만 전체 코드 흐름이 잘 보이지 않는 것 같아 주석이 없는 코드도 밑에 넣어놨다.</p>
<p>최선의 코드는 아닌 것 같지만... DFS를 연습하는 문제로는 정말 괜찮았다. 이런 방법은 어떻게 이론으로 고안한건지 ㅋㅋ. 어렵지만 더 열심히 공부해야겠다.😆</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 모의고사 - JAVA]]></title>
            <link>https://velog.io/@mandarine_punch/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-</link>
            <guid>https://velog.io/@mandarine_punch/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-</guid>
            <pubDate>Sun, 06 Mar 2022 10:06:27 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p><img src="https://images.velog.io/images/mandarine_punch/post/1c38da61-4349-47b0-bb47-3adf1a08f88d/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%AA%A8%EC%9D%98%EA%B3%A0%EC%82%AC.PNG" alt=""></p>
<hr>
<p>반복되는 배열을 쉽게 나타낼 수 있을 것 같은데, 뭔가... 노가다성 방법 말고는 생각이 나지 않았다. 그래서 생각하다 고안한 방법은 다음 설명과 같다! (다시 봐도 정말 이상한 방법이다 ㅋㅋ)</p>
<p><strong>1. 배열을 반복할 수 없으므로 repeat를 쓸 수 있는 String으로 배열을 바꾼다.</strong>
<strong>2. 그 후 문자열의 길이가 답지 길이보다 작으면 정의한 함수만큼 반복 후 답지 길이와 같게 자른다.</strong>
<strong>3. 자른 문자열을 다시 String 배열로 바꾸고 int 배열로 형변환한다.</strong>
<strong>4. 답지와 배열을 비교 후 많이 맞춘 사람을 return한다.</strong></p>
<h3 id="최초-통과-코드">최초 통과 코드</h3>
<pre><code class="language-java">import java.util.*;

class Solution {
    public String repeatNum(String str, int len){
        return len &gt; str.length() ? str.repeat(len/str.length() + 1) : str;
    }

    public int[] solution(int[] answers) {
        int len = answers.length;
        int count1=0, count2=0, count3=0;
        int[][] math = {
            {1,2,3,4,5},
            {2,1,2,3,2,4,2,5},
            {3,3,1,1,2,2,4,4,5,5}
        };

        for(int i=0;i&lt;math.length;i++){
            // 다시 봐도 웃긴 로직 ㅋㅋ
            String strMath = Arrays.toString(math[i]).replaceAll(&quot;[^0-9]&quot;,&quot;&quot;);
            String[] strArrMath = repeatNum(strMath, len).substring(0, len).split(&quot;&quot;);
            math[i] = Arrays.stream(strArrMath).mapToInt(Integer::parseInt).toArray();
        }

        for(int i=0;i&lt;len;i++){
            if(answers[i] == math[0][i]) count1++;
            if(answers[i] == math[1][i]) count2++;
            if(answers[i] == math[2][i]) count3++;
        }

        List&lt;Integer&gt; list = new ArrayList&lt;&gt;();
        int maxNum = Math.max(count1, Math.max(count2, count3));
        if(maxNum == count1) list.add(1);
        if(maxNum == count2) list.add(2);
        if(maxNum == count3) list.add(3);

        return list.stream().mapToInt(e -&gt; e).toArray();
    }
}</code></pre>
<p>놀랍게도 런타임 에러가 뜨지 않고 통과한 코드이다. ㅋㅋㅋ
자바스크립트가 주 언어였다보니 생각하는 방식이 이상해서 이런 괴랄한 방식이 나온 것 같다.
<span style="color: tomato"><strong>(자바스크립트는 자료형 구분이 거의 없다시피 한다.)</strong></span></p>
<p>통과 후 다른분의 코드를 보니... 코자타임이 와버렸다 ㅋㅋ</p>
<h3 id="짱짱-코드">짱짱 코드</h3>
<pre><code class="language-java">import java.util.ArrayList;

class Solution {
    public int[] solution(int[] answer) {

        int[] a = {1, 2, 3, 4, 5};
        int[] b = {2, 1, 2, 3, 2, 4, 2, 5};
        int[] c = {3, 3, 1, 1, 2, 2, 4, 4, 5, 5};
        int[] score = new int[3];

        for(int i=0; i&lt;answer.length; i++) {
            if(answer[i] == a[i%a.length]) {score[0]++;}
            if(answer[i] == b[i%b.length]) {score[1]++;}
            if(answer[i] == c[i%c.length]) {score[2]++;}
        }

        ArrayList&lt;Integer&gt; list = new ArrayList&lt;&gt;();
        int maxScore = Math.max(score[0], Math.max(score[1], score[2]));
        if(maxScore == score[0]) {list.add(1);}
        if(maxScore == score[1]) {list.add(2);}
        if(maxScore == score[2]) {list.add(3);}

        return list.stream().mapToInt(i-&gt;i.intValue()).toArray();
    }
}</code></pre>
<p>무작정 배열을 반복시킨 후 자를 생각만 했는데, 나머지 수식(%)을 사용할 줄은 몰랐다.
세상은 넓고 똑똑한 사람은 많은 것 같다.😂</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 더 맵게 - JAVA]]></title>
            <link>https://velog.io/@mandarine_punch/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%94-%EB%A7%B5%EA%B2%8C-JAVA</link>
            <guid>https://velog.io/@mandarine_punch/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%94-%EB%A7%B5%EA%B2%8C-JAVA</guid>
            <pubDate>Sat, 05 Mar 2022 16:44:32 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p><img src="https://images.velog.io/images/mandarine_punch/post/2f15efc7-59a8-4c36-8bc7-b9e37d322179/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%94%20%EB%A7%B5%EA%B2%8C(%EB%AC%B8%EC%A0%9C).PNG" alt=""></p>
<p><img src="https://images.velog.io/images/mandarine_punch/post/33eb518e-4e4f-47fb-a797-12ab6560efac/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%94%20%EB%A7%B5%EA%B2%8C(%EB%AC%B8%EC%A0%9C2).PNG" alt=""></p>
<hr>
<p>알고리즘 자체는 어렵지 않았다.
<strong>&#39;배열을 오름차순으로 정렬 후 맨 앞의 값과 그 다음 값을 생각하며 스코빌 지수(K)와 비교하면 되겠다!&#39;</strong></p>
<p>그런데 제한 사항에 있는 어마어마한 배열 길이를 보고 <strong>&#39;단순 정렬을 하면 너무 오래 걸리겠구나&#39;</strong>라는 판단을 했다.</p>
<p>그래서 자바의 배열 종류를 찾아보니 자동으로 추가한 값을 정리해 주는 PriorityQueue를 발견했다.</p>
<h3 id="최초-통과-코드">최초 통과 코드</h3>
<pre><code class="language-java">import java.util.*;

class Calc{
    // 짜 놓은 코드가 2번 이상 식을 사용하기에 method로 만들어줬다.
    public static Integer hotSum(int min1, int min2) {
        return min1 + (min2 * 2);
    }
}

class Solution {
    public int solution(int[] scoville, int K) {
        int answer = 0;
        // 짱짱 queue를 만들어준다. 기본적으로 안에 있는 값을 오름차순으로 정렬해준다.
        PriorityQueue&lt;Integer&gt; pq = new PriorityQueue&lt;&gt;();

        for(int i=0;i&lt;scoville.length;i++){
            pq.add(scoville[i]);
        }
        // 정렬된 queue의 첫 값과 그 다음 값을 추출한다.
        int min1 = pq.poll();
        int min2 = pq.poll();

        while(min1 &lt; K){
            // 이 반복문에 들어왔다는 것은 연산이 무조건 한 번 이상 시행될 것이라는 뜻이므로(answer++)
            answer++;
            // queue에 연산 값을 추가한다.
            pq.add(Calc.hotSum(min1, min2));
            // 자동으로 정렬이 됐으므로 그 다음 최솟값을 뽑는다.
            min1 = pq.poll();
            // 최솟값이 K보다 크면 바로 답을 반환
            if(min1 &gt; K) return answer;
            // 그렇지 않으면 두 번째 값도 추출한다.
            min2 = pq.poll();
            // 그 후 queue가 비어있다면 마지막 연산이라는 뜻이므로
            if(pq.isEmpty()) {
                // 그 결과를 반환해준다.
                return calc.hotSum(min1, min2) &gt; K ? ++answer : -1;
            }
        }
        return -1;
    }
}</code></pre>
<p>코드가 예상외로 빨리 통과가 돼서 중복되는 감이 있는 코드(min &lt; K, min &gt; K..)들이 있다.
그리고 조금 더 찾아봤는데 queue에는 첫 번째 값을 뽑아주는 poll만 있는 줄 알았는데, 첫 번째 값을 추출하지 않고 값만 가져오는 peek메서드도 있었다...(눈물 줄줄)</p>
<p>언어를 익힐 때, 조금 빨리 익혀보려고 코딩테스트로 공부하는 편인데... 이래서 야매 코딩은 무섭다. peek메서드를 안 이상 이렇게 넘어갈 순 없어서 코드를 다시 짜고 정리해봤다.</p>
<h3 id="최종-코드">최종 코드</h3>
<pre><code class="language-java">import java.util.*;

class Calc{
    public static Integer hotSum(int min1, int min2){
        return min1 + (min2 * 2);
    }
}

class Solution {
    public int solution(int[] scoville, int K) {
        int answer = 0;
        PriorityQueue&lt;Integer&gt; pq = new PriorityQueue&lt;&gt;();

        for(int i=0;i&lt;scoville.length;i++){
            pq.add(scoville[i]);
        }
        // 처음과 달리 queue를 변경하지 않고 첫 번째 값만 가져올 수 있어 조건을 바꿨다.
        // queue가 비어 있을 경우 값을 추출하면 error가 나기에
        // isEmpty() 보다는 queue.size가 1이 되면 탈출하게끔 추가해줬다.
        while(pq.peek() &lt; K &amp;&amp; pq.size() &gt; 1){
            answer++;
            // 구현한 메소드에 최솟값, 그 다음 값을 넣어 연산후 queue에 추가해준다.
            pq.add(Calc.hotSum(pq.poll(), pq.poll()));
        }
        // 최종 값이 K보다 크면 answer를 아니면 -1을 반환한다.
        return pq.peek() &gt; K ? answer : -1;
    }
}</code></pre>
<p>peek메서드를 이용하니 처음 코드보다 훨씬 간결해졌다..! 역시 아는 것이 힘이다.😆</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[코딩을 다시 시작한 이유😁]]></title>
            <link>https://velog.io/@mandarine_punch/%EB%82%B4%EA%B0%80-%EC%BD%94%EB%94%A9%EC%9D%84-%EB%8B%A4%EC%8B%9C-%EC%8B%9C%EC%9E%91%ED%95%9C-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@mandarine_punch/%EB%82%B4%EA%B0%80-%EC%BD%94%EB%94%A9%EC%9D%84-%EB%8B%A4%EC%8B%9C-%EC%8B%9C%EC%9E%91%ED%95%9C-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Sat, 05 Mar 2022 09:39:31 GMT</pubDate>
            <description><![CDATA[<p>처음 프로그래밍 언어를 접한 건 학교에서 들은 C와 C++ 이었다.</p>
<p>언어를 배우는 데에 있어 과정은 나쁘지 않았지만, 듣다 보니 이 언어를 어디에, 어떻게 써야 하는 건지 감이 잡히지 않았고 뜬구름 잡듯 이론만 익히는 게 지루하여 그만두었었다.</p>
<p>시간이 흘러 한 친구의 제안으로 같이 게임 개발을 하게되었다.
개발 지식이 많이 없어졌기에 나는 스토리 및 기획을 맡았고 전반적인 로직은 친구가 구현하였다.
원하는 기능과 이벤트가 하나씩 구현됨에 따라 즐거워하는 우리의 모습을 보고 <strong>&#39;어... 나도 코드를 짜면서 재밌었던 때가 있었는데&#39;</strong> 라는 생각이 스쳐 지나갔다.</p>
<p>그 생각 이후, 코딩을 다시 배우고 싶다는 욕구가 물밀듯 샘솟았다.</p>
<p>내가 코딩을 그만두게 된 이유가 어디에, 어떻게 적용해야 하는지 몰라서 였기 때문에, 
<strong>&#39;당장 눈앞에 결과를 볼 수 있는 웹 페이지 개발을 해봐야 겠다!&#39;</strong> 라고 마음을 먹고 이것저것 찾아봤다.</p>
<p>다시 시작하며 접한 언어는 html, css, javascript였다.</p>
<p>웹 페이지를 만들기 위해서는 javascript 이전에 html, css를 알아야 했고 크게 어렵지 않아 html, css만 이용하여 꽤 만족스러운 웹 페이지를 만들 수 있었다.</p>
<p>결과물을 보며 희열이라는 감정을 정말 오랜만에 느꼈다. 
그 후, interactive한 페이지를 만들고 싶어 javascript를 이용해 여러 event를 추가하여 페이지를 개선해 나갔다.</p>
<p>그렇게 node.js, mongodb, express 등과 같은 js관련 백엔드에 대해서도 알게 되면서 더욱 발전하는듯했으나, 만들다 보니 문득 이런 생각이 들었다.</p>
<p><strong>&#39;이렇게 만들어서 뭐 할 거야? 취업할 때 보여줄 수나 있을까?&#39;</strong></p>
<p>취업에 관한 어떤 정보도 없어서인지, 어디까지가 끝이고 어디까지 만들어놔야 취업할 때 보여줄 수 있는지에 대한 상한선이 확립이 되지 않았다.</p>
<p>더군다나, 찾아보는 자료에서는 <strong>&#39;우리나라에서는 무조건 Java를 배워야한다.&#39;</strong> 라고 모든 사람들이 말하고 있는 듯한 느낌이어서 <strong>&#39;지금까지 헛공부했나..&#39;</strong> 라는 생각이 들었다.</p>
<p>그 후, 혼자서는 객체지향 언어를 공부하기 버겁겠다는 판단을 내리고, 국비지원을 받아 학원에 등록하였다. 학원에서 면접을 보며 지금까지 공부한 내용들을 한탄하듯 전부 읊조렸다. <strong>&#39;길을 잘못 들었다.&#39;</strong> 라는 내 생각과 달리 학원에서는 <strong>&#39;잘 하고 있었네요!&#39;</strong> 하고 칭찬해 주며 잘 왔다고 격려해 주었다.</p>
<p>약간 울컥했다 ㅋㅋ</p>
<p>아직 학원을 다닌 지 이틀밖에 안됐지만, 과거와는 다르게 지금의 나는 취업이라는 확고한 목표를 가지고 있고 결과물을 만드는 재미를 알기에 잘 나아갈 것이라는 확신이 든다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 프린터 - JAVA]]></title>
            <link>https://velog.io/@mandarine_punch/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%94%84%EB%A6%B0%ED%84%B0-JAVA</link>
            <guid>https://velog.io/@mandarine_punch/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%94%84%EB%A6%B0%ED%84%B0-JAVA</guid>
            <pubDate>Sat, 05 Mar 2022 08:00:38 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p><img src="https://images.velog.io/images/mandarine_punch/post/8e804cf7-2b07-4bcd-b571-647f00fbd229/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%94%84%EB%A6%B0%ED%84%B0.PNG" alt=""></p>
<p><img src="https://images.velog.io/images/mandarine_punch/post/8bd38440-75c5-4eaa-aa5b-78480fd05eb1/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%94%84%EB%A6%B0%ED%84%B0-%EC%9E%85%EC%B6%9C%EB%A0%A5%EC%98%88.PNG" alt=""></p>
<hr>
<p>문제를 읽어보면 location이 priorities배열의 index를 가리키고 있다는 것을 알 수 있다.
이 점을 어떻게 이용할까 고민하다 배열의 index와 그 index가 가리키고 있는 값을 키와 값으로 묶어 Hashmap으로 구현해 보기로 마음먹고 문제를 풀었다.</p>
<h3 id="코드-분석">코드 분석</h3>
<pre><code class="language-java">import java.util.*;

class Solution {
    public int solution(int[] priorities, int location) {
        int answer = 0;
        Map&lt;Integer, Integer&gt; printHm = new HashMap&lt;&gt;();
        // 배열의 index를 key, 그 값을 value로 분류
        for(int i=0;i&lt;priorities.length;i++){
            printHm.put(i, priorities[i]);
        }
        // 최우선으로 인쇄하는 값을 지정해줬다. 
        int topPriority = Collections.max(printHm.values());

        while(true){
            for(int key : printHm.keySet()){
                // 배열을 돌다 나온 값이 최우선 순위와 같을 경우
                if(printHm.get(key) == topPriority){
                    // 최우선 순위 값이 먼저 인쇄되므로 1장을 인쇄한다(answer++).
                    answer++;
                    // 인쇄 후 그 key와 location이 같을 경우
                    if(key == location){
                        // 찾고 있던 값이므로 답을 반환!
                        return answer;
                    }
                    // 그렇지 않으면 해당 key에 있는 값을 0이라는 최솟값으로 두고
                    printHm.replace(key, 0);
                    // 최우선 순위를 다시 설정해준다.
                    topPriority = Collections.max(printHm.values());
                }
            }
        }
    }
}</code></pre>
<p>while 조건문을 세울 때 일단 돌리고 보자는 마음에 true로 설정해 두었는데,
코드를 짜다보니 결국 안쪽에서 답을 반환해줘서 true로 돌려도 괜찮겠다는 마음에 그대로 두었다.</p>
<h3 id="최종-코드">최종 코드</h3>
<pre><code class="language-java">import java.util.*;

class Solution {
    public int solution(int[] priorities, int location) {
        int answer = 0;
        Map&lt;Integer, Integer&gt; printHm = new HashMap&lt;&gt;();

        for(int i=0;i&lt;priorities.length;i++){
            printHm.put(i, priorities[i]);
        }

        int topPriority = Collections.max(printHm.values());

        while(true){
            for(int key : printHm.keySet()){
                if(printHm.get(key) == topPriority){
                    answer++;
                    if(key == location){
                        return answer;
                    }
                    printHm.replace(key, 0);
                    topPriority = Collections.max(printHm.values());
                }
            }
        }
    }
}</code></pre>
<p>나름 직관적이고 깔끔하게 나온 것 같다! ㅎㅎ
문제를 풀면서 느끼는 건 자료형이 확실히 정해져 있는 언어로 코드를 짜는게 생각보다 어렵다는 것이다..😂</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[오래 걸리는 건 당연한가..?(코딩)]]></title>
            <link>https://velog.io/@mandarine_punch/%EC%98%A4%EB%9E%98-%EA%B1%B8%EB%A6%AC%EB%8A%94-%EA%B1%B4-%EB%8B%B9%EC%97%B0%ED%95%9C%EA%B0%80..%EC%BD%94%EB%94%A9</link>
            <guid>https://velog.io/@mandarine_punch/%EC%98%A4%EB%9E%98-%EA%B1%B8%EB%A6%AC%EB%8A%94-%EA%B1%B4-%EB%8B%B9%EC%97%B0%ED%95%9C%EA%B0%80..%EC%BD%94%EB%94%A9</guid>
            <pubDate>Fri, 04 Mar 2022 17:31:12 GMT</pubDate>
            <description><![CDATA[<p>많은 코딩을 해본 것은 아니지만,
여러 알고리즘 문제를 풀거나 개인 토이(toy) 프로젝트를 진행했을 때 그냥 구현만 되면 됐지 하고 넘어가면 코드가 꽤 지저분해진다는 것을 알았다.</p>
<p>처음에는 그냥 그러려니 했으나, 여러 method들이 쌓이면서 이해하려면 처음부터 다시 읽어야하는 불상사가 발생하여, 코딩하는 시간이 이상한 방향으로 늦어지는 것을 느꼈다.</p>
<p>그 후, 코드를 작성할 때 처음에는 생각나는 모든 코드를 작성한 뒤 코드가 정상적으로 작동하면, 그 코드를 정리하는 방향으로 스타일을 바꿨다.</p>
<p>좋은 방법인지는 모르겠으나, 코드를 짤 때만큼 정리하는데도 그만큼의 시간을 투자하여 정리하였다.</p>
<p>과정이 오래 걸리지만 후에 코드를 다시 볼 때 처음처럼 다시 다 읽을 필요가 없어져서 괜찮은 것도 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 주식 가격 -JAVA]]></title>
            <link>https://velog.io/@mandarine_punch/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%A3%BC%EC%8B%9D-%EA%B0%80%EA%B2%A9-JAVA</link>
            <guid>https://velog.io/@mandarine_punch/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%A3%BC%EC%8B%9D-%EA%B0%80%EA%B2%A9-JAVA</guid>
            <pubDate>Fri, 04 Mar 2022 16:45:12 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p><img src="https://images.velog.io/images/mandarine_punch/post/522b637b-6547-41c1-9ead-7e5e0715d733/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%A3%BC%EC%8B%9D%EA%B0%80%EA%B2%A9.PNG" alt=""></p>
<hr>
<p>여기까지가 설명인데... 참 설명이 애매해서 이해하는데 시간이 좀 걸렸다. 어느 훌륭하신 분이 문제를 잘 설명해 주셨는데 그 내용은 다음과 같다.</p>
<ul>
<li>n초 간의 주가를 초 단위로 기록한 배열 prices가 매개변수로 주어질 때, 각 초의 주가를 기준으로 해당 초 부터 n초 사이에 가격이 떨어지지 않은 시간은 몇 초인지 배열에 담아 return 하도록 solution 함수를 완성하세요.</li>
</ul>
<pre><code class="language-java">class Solution {
    public int[] solution(int[] prices) {
        int[] answer = new int[prices.length];
        // prices 배열과 비교해주기 위해 복사
        int[] priceClone = prices.clone();

        // 결국 배열의 마지막 값은 비교대상이 없어 항상 0이므로
        // 효율성을 조금 높여보고자 마지막 배열 반복을 생략했다.
        for(int i=0;i&lt;prices.length-1;i++){
            // 아주 약간 헷갈렸는데 j=i+1로 지정해 줌으로써 지나간 배열을 다시 순회하지 않는다.
            for(int j=i+1;j&lt;prices.length;j++){
                answer[i]++;
                // 주식이 떡락했다면😢 반복문을 탈출한다.
                if(prices[i] &gt; priceClone[j]){
                    break;
                }
            }
        }

        return answer;
    }
}</code></pre>
<p>초기 통과 코드이다. 로직을 순차적으로 해결하다보니 코드가 꽤 간결해졌다. 그럼에도 불구하고 코드를 보면 한 가지 의문이 든다.</p>
<ul>
<li>비교를 위해 굳이 배열을 복사해줘야할까..?</li>
</ul>
<p>맞다. 안해줘도 된다. 사실 if(prices[i] &gt; price[j])로 <span style="color: red">&#39;s&#39;</span> 를 빼먹은 오타를 낸 바람에 <span style="color: red">&#39;아! 같은 배열은 비교가 안되는구나&#39;</span> 라고 착각하여 벌어진 실수이다. 그래서 코드를 좀 더 깨끗하게 재작성해봤다.</p>
<pre><code class="language-java">class Solution {
    public int[] solution(int[] prices) {
        int[] answer = new int[prices.length];

        for(int i=0;i&lt;prices.length-1;i++){
            for(int j=i+1;j&lt;prices.length;j++){
                answer[i]++;
                if(prices[i] &gt; prices[j]){
                    break;
                }
            }
        }

        return answer;
    }
}</code></pre>
<p>조금 더 깔끔해지니 보기도 좋고 기분도 좋다. ㅎㅎ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 다리를 지나는 트럭 - JAVA]]></title>
            <link>https://velog.io/@mandarine_punch/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8B%A4%EB%A6%AC%EB%A5%BC-%EC%A7%80%EB%82%98%EB%8A%94-%ED%8A%B8%EB%9F%AD-JAVA</link>
            <guid>https://velog.io/@mandarine_punch/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8B%A4%EB%A6%AC%EB%A5%BC-%EC%A7%80%EB%82%98%EB%8A%94-%ED%8A%B8%EB%9F%AD-JAVA</guid>
            <pubDate>Fri, 04 Mar 2022 15:15:01 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p><img src="https://images.velog.io/images/mandarine_punch/post/7af91b54-de8d-4ec5-a996-01a851e3cab9/%ED%8A%B8%EB%9F%AD%20%EB%AC%B8%EC%A0%9C.PNG" alt=""></p>
<hr>
<p>하중과 길이가 정해진 다리에 트럭 여러 대가 건너가려고 할 때, 걸리는 최소 시간을 구하는 문제이다. </p>
<p>다리 길이(<span style="color:blue"><strong>bridge_length</strong></span>), 버틸 수 있는 하중(<span style="color:blue"><strong>weight</strong></span>), 트럭의 수와 무게를 알 수 있는 배열(<span style="color:blue"><strong>truck_weights</strong></span>)이 값으로 주어졌다.</p>
<pre><code class="language-java">import java.util.*;

class Solution {
    public int solution(int bridge_length, int weight, int[] truck_weights) {
        int answer = 0;
        Queue&lt;Integer&gt; bridge = new LinkedList&lt;&gt;(); 
        // 비어있는 다리의 공간을 0으로 가득 채웠다.
        for(int i=0;i&lt;bridge_length;i++){
            bridge.offer(0);
        }

        int index = 0;
        // 다리에 있는 현재 트럭의 무게이다.
        int currentWeight = 0;
        // 트럭이 더이상 남아있지 않을 때 탈출해주기 위해 조건을 만들었다.
        while(index &lt; truck_weights.length){
            // 현재 다리에 있는 트럭무게에서 곧 나갈 트럭의 무게를 빼준다.
            currentWeight -= bridge.poll();
            // 새 트럭이 들어올 것이므로 1초를 추가한다.
            answer++;
            // 현재 다리에 있는 트럭 무게와 곧 들어올 트럭 무게의 합과 다리의 하중을 비교
            if(currentWeight + truck_weights[index] &lt;= weight){
                // 무게를 버틴다면 다리에 트럭을 추가한다.
                bridge.offer(truck_weights[index]);
                // 현재 다리에 있는 트럭 무게에도 새 트럭 무게를 더해준다.
                // 그리고 다음 트럭을 지정하기 위해 후위 연산자를 써주어 index를 증가시켰다.
                currentWeight += truck_weights[index++];
            } else{
                // 버티지 못한다면 0을 추가한다.
                bridge.offer(0);
            }
        }
        //처음 설정한 0으로 채워진 다리가 전부 치환되면 결국 처음 다리 길이와 같으므로
        //트럭이 지나간 시간 + 다리 길이
        return answer + bridge_length; 
    } 
}</code></pre>
<p>최종 코드이다. java를 이제 막 공부하고 있는 입장이라 queue를 처음 써보지만 문제 유형이 스택/큐로 분류되어 있어 queue를 이용하여 풀어봤다.</p>
<pre><code class="language-java">while(index &lt; truck_weights.length){
    answer++;
    currentWeight -= bridge.poll();
    if(currentWeight + truck_weights[index] &lt;= weight){
        bridge.offer(truck_weights[index]);
        currentWeight += truck_weights[index++];
    } else{
        bridge.offer(0);
    }
}</code></pre>
<p>이 로직을 짤 때 생각을 많이 했는데, 결국 문제 그대로 다리에서 트럭을 넣고 빼는 것이 가장 직관적이고 구현이 쉬워 이렇게 구현했다.</p>
]]></description>
        </item>
    </channel>
</rss>