diff --git a/README.md b/README.md index e078fd41..2405b132 100644 --- a/README.md +++ b/README.md @@ -1 +1,137 @@ # javascript-racingcar-precourse +--- +## πŸ’‘ μ™œ κ΅¬ν˜„μ„ ν•΄μ•Ό ν•˜λŠ”κ°€ +두 번째 κ³Όμ œλŠ” μžλ™μ°¨ κ²½μ£Ό κ²Œμž„μ„ κ΅¬ν˜„ν•˜λŠ” κ²ƒμž„. κ·Έ μ€‘μ—μ„œλ„ 이번 과제의 λͺ©ν‘œκ°€ β€œν° ν•¨μˆ˜λ₯Ό 단일 역할을 μˆ˜ν–‰ν•˜λŠ” μž‘μ€ ν•¨μˆ˜λ‘œ λΆ„λ¦¬β€ν•˜λŠ” κ²ƒμ΄λΌλŠ” 점을 λ³΄μ•˜μŒ.μ²˜μŒμ—λŠ” λ‹¨μˆœνžˆ 랜덀 값을 뽑아 μžλ™μ°¨λ₯Ό 움직이면 λ˜λŠ” 문제라고 μƒκ°ν–ˆμœΌλ‚˜, μ‹€μ œλ‘œ κ΅¬ν˜„μ„ μ§„ν–‰ν•˜λ €κ³  λ³΄λ‹ˆ μ–΄λ–»κ²Œ ν•¨μˆ˜λ₯Ό 뢄리해야 쒋은 ꡬ쑰가 λ˜λŠ”μ§€ κΆκΈˆν•΄μ‘ŒμŒ. κ·Έλž˜μ„œ λ‹¨μˆœνžˆ ν•¨μˆ˜λ₯Ό μž‘κ²Œ μͺΌκ°œλ©΄ λ˜λŠ” 것인지 μ•„λ‹ˆλ©΄ λ‹€λ₯Έ 기쀀이 ν•„μš”ν•œ 것인지에 λŒ€ν•΄ κ³ λ―Όν•˜κ²Œ λ˜μ—ˆκ³  자료λ₯Ό μ°Ύμ•„λ³΄κ²Œ λ˜μ—ˆμŒ. + +--- +## κ΄€λ ¨ λ…Όλ¬Έ μš”μ•½ +κ·Έ μ€‘μ—μ„œλ„ λ‚˜μ˜ κΆκΈˆμ¦μ„ ν’€μ–΄μ€€ 연ꡬ μžλ£Œκ°€ μžˆμ—ˆμŒ. + +μ—°κ΅¬μ—μ„œλŠ” 두 κ°€μ§€ κΈ°λŠ₯을 λΉ„κ΅ν–ˆμŒ. +ν•˜λ‚˜λŠ” λ‚ μ§œ 차이λ₯Ό κ³„μ‚°ν•˜λŠ” λ‹¨μˆœν•œ μ ˆμ°¨ν˜• κΈ°λŠ₯(μž…λ ₯ β†’ 계산 β†’ 좜λ ₯처럼 단계가 λͺ…ν™•ν•œ ꡬ쑰)μ΄μ—ˆκ³ , λ‹€λ₯Έ ν•˜λ‚˜λŠ” 쑰건문과 데이터 흐름이 λ³΅μž‘ν•œ κΈ°λŠ₯(μ—¬λŸ¬ 상황을 λΆ„κΈ° μ²˜λ¦¬ν•΄μ•Ό ν•˜λŠ” ꡬ쑰)μ΄μ—ˆμŒ. + +μ‹€ν—˜ κ²°κ³Ό, μ „μžμ˜ κ²½μš°μ—λŠ” μ—¬λŸ¬ ν•¨μˆ˜λ₯Ό λ‚˜λˆ„μ–΄ κ΅¬ν˜„ν•œ 방식이 μ΄ν•΄ν•˜κΈ° 더 μ‰¬μ› μŒ. +ν•˜μ§€λ§Œ ν›„μžμ˜ κ²½μš°μ—λŠ” 였히렀 ν•¨μˆ˜κ°€ λ§Žμ•„μ§ˆμˆ˜λ‘ 흐름을 따라가기 μ–΄λ €μ›Œμ Έ ν•˜λ‚˜μ˜ ν•¨μˆ˜λ‘œ κ΅¬ν˜„ν•œ μ½”λ“œκ°€ 더 μ‰½κ²Œ μ΄ν•΄λ˜μ—ˆμŒ. + +λ”°λΌμ„œ κΈ°λŠ₯의 성격에 따라 κ²°κ³Όκ°€ λ‹¬λžμœΌλ©°, +β€œν•¨μˆ˜λ₯Ό 잘게 μͺΌκ°œλ©΄ 항상 이해가 μ‰¬μ›Œμ§„λ‹€β€λŠ” 일반적인 법칙은 μ„±λ¦½ν•˜μ§€ μ•Šμ•˜μŒ. +결둠적으둜, ν•¨μˆ˜ λΆ„ν•΄λŠ” λ§₯락 의쑴적이며, +μ½”λ“œμ˜ λͺ©μ Β·λ°μ΄ν„° 흐름 λ³΅μž‘λ„Β·λ³€κ²½ λΉˆλ„ 등을 κΈ°μ€€μœΌλ‘œ κ²°μ •ν•΄μ•Ό ν•œλ‹€κ³  μ œμ•ˆν•¨. + +> **좜처:** +> Tempero, E., Denny, P., Finnie-Ansley, J., Luxton-Reilly, A., et al. (2024). +> *On the Comprehensibility of Functional Decomposition: An Empirical Study.* +> In *Proceedings of the 32nd IEEE/ACM International Conference on Program Comprehension (ICPC ’24)*, 214–224. IEEE/ACM. +> [https://doi.org/10.1145/3643916.3644432] +--- + +## ꡬ쑰 섀계 및 파일 뢄리 κΈ°μ€€ +λ”°λΌμ„œ 이번 κ³Όμ œμ—μ„œλŠ” ν•˜λ‚˜μ˜ 파일 μ•ˆμ— λͺ¨λ“  κΈ°λŠ₯을 λ„£μ§€ μ•Šκ³  κΈ°λŠ₯ λ‹¨μœ„λ‘œ νŒŒμΌμ„ λΆ„λ¦¬ν•˜κ³ , 각 파일 λ‚΄λΆ€μ—μ„œλ„ 역할별 ν•¨μˆ˜λ‘œ μ„ΈλΆ„ν™”ν–ˆμŒ. +νŒŒμΌμ€ 크게 μž…λ ₯ / 검증 / κ²½μ£Ό / 좜λ ₯ / μ‹€ν–‰ 흐름 κ΄€λ¦¬λ‘œ λ‚˜λˆ΄κ³ , + +각 파일 λ‚΄λΆ€ ν•¨μˆ˜λŠ” λ…Όλ¬Έ 기쀀에 따라 +μ ˆμ°¨ν˜• λ‹¨κ³„λŠ” μž‘κ²Œ 뢄리, λΆ„κΈ°Β·μ˜ˆμ™Έκ°€ λ§Žμ€ λ‘œμ§μ€ μ‘μ§‘ν•˜λŠ” λ°©ν–₯으둜 κ΅¬μ„±ν–ˆμŒ. + +1. App.js +App은 ν”„λ‘œκ·Έλž¨μ˜ μ‹œμž‘μ μ΄λΌ, +λ‘œμ§μ„ 넣기보단 전체 νλ¦„λ§Œ κ΄€λ¦¬ν•˜λ„λ‘ λ‹¨μˆœν•˜κ²Œ μœ μ§€ν•˜λŠ” 게 λ§žλ‹€κ³  νŒλ‹¨ν•¨. + +2. input.js +μ‚¬μš©μž μž…λ ₯을 λ°›λŠ” 뢀뢄은 μ ˆμ°¨ν˜• λ‹¨κ³„λ‘œ ꡬ뢄함. +μžλ™μ°¨ 이름을 λ°›κ³ , κ·Έ λ‹€μŒ μ‹œλ„ 횟수λ₯Ό λ°›λŠ” μˆœμ„œκ°€ κ³ μ •λ˜μ–΄ μžˆμœΌλ―€λ‘œ 각 μž…λ ₯ 과정을 λ³„λ„μ˜ λ‹¨κ³„λ‘œ λ‚˜λˆ„λŠ” 것이 μžμ—°μŠ€λŸ¬μ›€. + +3. validate.js +μž…λ ₯κ°’ 검증은 μ—¬λŸ¬ 쑰건을 λ™μ‹œμ— 확인해야 ν•˜λ―€λ‘œ λΆ„κΈ°ν˜• λ‘œμ§μž„. μ˜ˆμ™Έ μΌ€μ΄μŠ€(빈 λ¬Έμžμ—΄, 이름 길이 초과, 숫자 μ•„λ‹˜ λ“±)κ°€ λ‹€μ–‘ν•΄μ„œ +ν•¨μˆ˜λ₯Ό μ„ΈλΆ„ν™”ν•˜λ©΄ 였히렀 흐름이 λŠμ–΄μ§ˆ κ°€λŠ₯성이 있음. κ·Έλž˜μ„œ β€œμžλ™μ°¨ 이름 전체 검증”과 β€œμ‹œλ„ 횟수 κ²€μ¦β€μ²˜λŸΌ μž…λ ₯ λ‹¨μœ„λ‘œ λ¬Άμ–΄μ„œ ν•œ λ²ˆμ— μ²˜λ¦¬ν•˜κΈ°λ‘œ 함. + +4. race.js +μžλ™μ°¨ κ²½μ£Ό λ‘œμ§μ€ μ—¬λŸ¬ λ‹¨κ³„λ‘œ μ΄λ£¨μ–΄μ§€λŠ” μ ˆμ°¨ν˜• νλ¦„μ΄λ―€λ‘œ λ‹¨κ³„λ³„λ‘œ λ‚˜λˆ„λŠ” 게 νš¨μœ¨μ μž„. + +- 전체 λΌμš΄λ“œλ₯Ό λ°˜λ³΅ν•˜λŠ” 단계 +- ν•œ λΌμš΄λ“œλ₯Ό μ‹€ν–‰ν•˜λŠ” 단계 +- μ „μ§„ν• μ§€ νŒλ‹¨ν•˜λŠ” μ •μ±… 단계 +특히 μ „μ§„ μ—¬λΆ€λ₯Ό κ²°μ •ν•˜λŠ” 랜덀 정책은 λ³€κ²½ κ°€λŠ₯성이 λ†’κΈ° λ•Œλ¬Έμ— λ³„λ„λ‘œ λΆ„λ¦¬ν•˜λŠ” λ°©μ‹μœΌλ‘œ 섀계함. + +5. output.js +좜λ ₯은 ν”„λ‘œκ·Έλž¨μ˜ λ§ˆμ§€λ§‰ μ ˆμ°¨ν˜• λ‹¨κ³„λ‘œ, μ½˜μ†”μ— κ²°κ³Όλ₯Ό ν‘œμ‹œν•˜λŠ” μ—­ν• λ§Œ 담당함. 둜직과 좜λ ₯이 μ„žμ΄λ©΄ ν…ŒμŠ€νŠΈν•˜κΈ° μ–΄λ €μ›Œμ§€λ―€λ‘œ μ™„μ „νžˆ 뢄리함. +λΌμš΄λ“œλ³„ 좜λ ₯κ³Ό μ΅œμ’… 우승자 좜λ ₯은 μ„œλ‘œ λ‹€λ₯Έ μ±…μž„μ΄λ―€λ‘œ 각각 λ…λ¦½μ μœΌλ‘œ λ‹€λ£¨λŠ” 것이 μ’‹λ‹€κ³  νŒλ‹¨ν–ˆμŒ. + +--- + +## βœ… κ΅¬ν˜„ 체크리슀트 + +### μž…λ ₯ 처리 +- [x] μ•ˆλ‚΄ 문ꡬ 좜λ ₯: `κ²½μ£Όν•  μžλ™μ°¨ 이름을 μž…λ ₯ν•˜μ„Έμš”.(이름은 μ‰Όν‘œ(,) κΈ°μ€€μœΌλ‘œ ꡬ뢄)` +- [x] μžλ™μ°¨ 이름 λ¬Έμžμ—΄ μž…λ ₯λ°›κΈ° +- [x] μ•ˆλ‚΄ 문ꡬ 좜λ ₯: `μ‹œλ„ν•  νšŸμˆ˜λŠ” λͺ‡ νšŒμΈκ°€μš”?` +- [x] μ‹œλ„ 횟수 μž…λ ₯λ°›κΈ° + +### μžλ™μ°¨ 이름 νŒŒμ‹± 및 검증 +- [x] μ‰Όν‘œ(`,`) κΈ°μ€€μœΌλ‘œ 이름 뢄리 +- [x] 각 이름 길이 1~5자 검증 +- [x] 빈 이름/연속 κ΅¬λΆ„μž(`pobi,,jun`) 검증 + +### μ‹œλ„ 횟수 검증 +- [x] μ •μˆ˜ μ—¬λΆ€ 확인 +- [x] 1 이상인지 확인 + +### μ „μ§„ 둜직 +- [x] λ§€ μ‹œλ„λ§ˆλ‹€ λͺ¨λ“  μžλ™μ°¨μ— λŒ€ν•΄ λ‚œμˆ˜ 생성 +- [x] λ‚œμˆ˜κ°€ 4이상이면 μ „μ§„, κ·Έ μ™Έ μ •μ§€ +- [x] μ „μ§„ μ‹œ ν•΄λ‹Ή μžλ™μ°¨μ˜ 이동 기둝에 `-` 1개 μΆ”κ°€ + +### μ°¨μˆ˜λ³„ μ‹€ν–‰ κ²°κ³Ό 좜λ ₯ +- [x] 첫 λΌμš΄λ“œ μ „ `μ‹€ν–‰ κ²°κ³Ό` 좜λ ₯ +- [x] 각 λΌμš΄λ“œ μ’…λ£Œ μ‹œ `이름 : -` ν˜•μ‹μœΌλ‘œ λͺ¨λ“  μžλ™μ°¨ μƒνƒœ 좜λ ₯ + +### 우승자 νŒλ³„ 및 좜λ ₯ +- [x] μ΅œμ’… μ΅œλŒ€ μ§„ν–‰ 거리 계산 +- [x] μ΅œλŒ€ 거리 동λ₯  μžλ™μ°¨ μ „λΆ€ 우승 처리 +- [x] `μ΅œμ’… 우승자 : 이름1, 이름2` ν˜•μ‹μœΌλ‘œ 좜λ ₯ + +### μ˜ˆμ™Έ 처리 (λ°œμƒ μ‹œ μ’…λ£Œ) +- [x] λͺ¨λ“  μ—λŸ¬ λ©”μ‹œμ§€λŠ” **`[ERROR]`** 둜 μ‹œμž‘ +- [x] 잘λͺ»λœ 이름 ν˜•μ‹(빈 토큰/연속 κ΅¬λΆ„μž) β†’ `[ERROR] 잘λͺ»λœ 이름 ν˜•μ‹μž…λ‹ˆλ‹€. +- [x] 이름 길이 초과(6자 이상) β†’ `[ERROR] μžλ™μ°¨ 이름은 5자 μ΄ν•˜λ§Œ κ°€λŠ₯ν•©λ‹ˆλ‹€. +- [x] μ‹œλ„ νšŸμˆ˜κ°€ 음수 or 0μΌλ•Œ β†’ `[ERROR] μ‹œλ„ νšŸμˆ˜λŠ” 1 μ΄μƒμ˜ μˆ«μžμ—¬μ•Ό ν•©λ‹ˆλ‹€.' +--- +## πŸ’­ μ½”λ“œ κ°œμ„  κ³ λ―Ό 1 πŸ€” +### 3ν•­ μ—°μ‚°μžλ₯Ό μ“°μ§€ μ•Šμ•„μ•Όν•˜λŠ” 이유 +μžλ°”μŠ€ν¬λ¦½νŠΈμ— 3ν•­ μ—°μ‚°μžκ°€ μžˆλŠ”λ°λ„ μ‚¬μš©μ„ μ€„μ—¬μ•Όν•˜λŠ” μ΄μœ λŠ” λ¬΄μ—‡μΌκΉŒ?λΌλŠ” 생각이 λ“€μ—ˆμŒ. + +MDN λ¬Έμ„œμ— λ”°λ₯΄λ©΄, if...elseλ₯Ό ν•œ μ€„λ‘œ 쀄이기 μœ„ν•œ 문법적 좕약이지, +μ—¬λŸ¬ 쑰건을 μ²˜λ¦¬ν•˜κ±°λ‚˜ 둜직 λΆ„κΈ°λ₯Ό ν‘œν˜„ν•˜κΈ° μœ„ν•œ ꡬ쑰가 μ•„λ‹˜. + +특히 μ‚Όν•­ μ—°μ‚°μžλŠ” 였λ₯Έμͺ½ κ²°ν•©ν˜•(right-associative) 이라 +쀑첩될 경우 a ? b : (c ? d : e)처럼 ν•΄μ„λ˜μ–΄ μ½”λ“œλ₯Ό 읽기 μ–΄λ ΅κ²Œ λ§Œλ“€ 수 있음. + +> **좜처:** +> Mozilla Contributors. (2025, July 8). Conditional (ternary) operator. +> In MDN Web Docs – Expressions and Operators. Mozilla Foundation. +> Retrieved from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_operator + +이에 따라 race.js / playRoundμ—μ„œ 3ν•­ μ—°μ‚°μž μ œκ±°ν–ˆμŒ. + +--- + +## πŸ’­ μ½”λ“œ κ°œμ„  κ³ λ―Ό 2 πŸ€” +### 랜덀 μ˜μ‘΄μ„± μ£Όμž… +전체 μ½”λ“œλ₯Ό λ‹€μ‹œ 보며 β€œκ° ν•¨μˆ˜κ°€ ν•œ κ°€μ§€ 일만 ν•˜λ„λ‘ 잘 λ‚˜λˆ μ‘ŒλŠ”κ°€?”λ₯Ό μ κ²€ν•˜λ˜ 쀑, +race.jsμ—μ„œ 랜덀 생성과 이동 κ·œμΉ™ μ μš©μ΄λΌλŠ” 두 κ°€μ§€ 역할이 ν•œ ν•¨μˆ˜μ— μ„žμ—¬ μžˆλ‹€λŠ” κ±Έ λ°œκ²¬ν•¨. +β€œν•¨μˆ˜(λ˜λŠ” λ©”μ„œλ“œ)κ°€ ν•œ κ°€μ§€ 일만 ν•˜λ„λ‘ μ΅œλŒ€ν•œ μž‘κ²Œ λ§Œλ“€μ–΄λΌ.β€λΌλŠ” μš”κ΅¬μ‚¬ν•­μ„ λ– μ˜¬λ¦¬κ³ , +두 역할을 뢄리해 ν•¨μˆ˜κ°€ ν•œ κ°€μ§€ 일만 μˆ˜ν–‰ν•˜λ„λ‘ μˆ˜μ •ν•¨. +랜덀 생성 뢀뢄을 λ§€κ°œλ³€μˆ˜(rng) 둜 λΆ„λ¦¬ν•˜μ—¬ μ™ΈλΆ€μ—μ„œ μ£Όμž…λ°›λ„λ‘ 변경함. + +--- + +## κ°œλ°œμ„ λ§ˆλ¬΄λ¦¬ν•˜λ©° +### 개발자의 κ΄€μ μ—μ„œμ˜ 생각 +이번 과제λ₯Ό μ§„ν–‰ν•˜λ©° λ‹¨μˆœνžˆ κΈ°λŠ₯을 κ΅¬ν˜„ν•˜λŠ” 것보닀, μ˜λ„ν•œ λŒ€λ‘œ μ •ν™•νžˆ λ™μž‘ν•˜λ„λ‘ ꡬ쑰λ₯Ό λ‹€λ“¬λŠ” κ³Όμ •μ˜ μ€‘μš”μ„±μ„ κΉ¨λ‹«κ²Œ λ˜μ—ˆμŒ. +μ²˜μŒμ—λŠ” μž‘μ€ μ‹€μˆ˜λ“€μ΄ μ˜ˆμƒλ³΄λ‹€ 큰 였λ₯˜λ‘œ μ΄μ–΄μ‘ŒμŒ. output.js 파일λͺ… μ˜€νƒ€(ouput.js)둜 인해 λͺ¨λ“ˆμ΄ λ‘œλ“œλ˜μ§€ μ•Šκ±°λ‚˜, λ³€μˆ˜λͺ… 뢈일치(car β†’ cars, distance β†’ c.distance)둜 좜λ ₯이 κΉ¨μ§€λŠ” λ¬Έμ œκ°€ μ—¬λŸ¬ 번 λ°œμƒν–ˆμŒ. λ˜ν•œ 우승자 좜λ ₯ μ‹œ 'Winners' β†’ 'winner'처럼 단어 ν•˜λ‚˜μ˜ 차이둜 κ²°κ³Όκ°€ λ‹¬λΌμ§€λŠ” 과정을 κ²ͺ으며, 좜λ ₯ 포맷의 일관성이 μ‚¬μš©μž 신뒰와 가독성에 직접적인 영ν–₯을 μ€€λ‹€λŠ” 점을 λ°°μ› μŒ. + +### μ„€κ³„μž κ΄€μ μ—μ„œμ˜ 생각 +μš°ν…Œμ½” 2μ£Ό μ°¨ 과제의 핡심 λͺ©ν‘œκ°€ β€œμ—¬λŸ¬ 역할을 μˆ˜ν–‰ν•˜λŠ” 큰 ν•¨μˆ˜λ₯Ό 단일 역할을 μˆ˜ν–‰ν•˜λŠ” μž‘μ€ ν•¨μˆ˜λ‘œ λΆ„λ¦¬ν•œλ‹€β€λŠ” μ μ΄μ—ˆμŒ. +λ”°λΌμ„œ 이번 κ³Όμ œμ—μ„œλŠ” λ‹¨μˆœνžˆ κΈ°λŠ₯을 λ‚˜λˆ„λŠ” 데 κ·ΈμΉ˜μ§€ μ•Šκ³ , μ–΄λ””κΉŒμ§€λ₯Ό λΆ„λ¦¬ν•˜κ³  μ–΄λ””μ„œλΆ€ν„° 응집해야 ν•˜λŠ”κ°€λ₯Ό 역할을 λ‚˜λˆ„λ©΄μ„œ κ³ λ―Όν•˜λ©° ꡬ쑰λ₯Ό μ„€κ³„ν–ˆμŒ. μ²˜μŒμ—λŠ” κ°€λŠ₯ν•œ ν•œ λͺ¨λ“  λ‘œμ§μ„ μ„ΈλΆ„ν™”ν•˜λ € ν–ˆμœΌλ‚˜ μ‹€μ œλ‘œλŠ” μž‘μ€ ν•¨μˆ˜λ‘œ λΆ„λ¦¬ν•˜λ©΄μ„œλ„ νλ¦„μ˜ μžμ—°μŠ€λŸ¬μ›€μ΄ μœ μ§€λ˜λ„λ‘ ν•˜λŠ” κ· ν˜•μ΄ μ€‘μš”ν•˜λ‹€λŠ” 것을 κΉ¨λ‹¬μ•˜μŒ. 이 κ²½ν—˜μ„ 톡해 ν•¨μˆ˜ λΆ„ν•΄λŠ” λ‹¨μˆœν•œ κ·œμΉ™μ΄ μ•„λ‹ˆλΌ 둜직의 성격과 데이터 νλ¦„μ˜ λ³΅μž‘λ„λ₯Ό κ³ λ €ν•΄μ•Ό ν•˜λŠ” 섀계적 κ²°μ •μž„μ„ λ°°μ› μŒ. + +### 기획자 κ΄€μ μ—μ„œμ˜ 생각 +λ‹¨μˆœνžˆ μžλ™μ°¨κ°€ 랜덀으둜 μ›€μ§μ΄λŠ” κ²Œμž„μœΌλ‘œ 끝내기보닀 λΌμš΄λ“œλ§ˆλ‹€ ν”Œλ ˆμ΄μ–΄κ°€ μ „λž΅μ μœΌλ‘œ 선택할 수 μžˆλŠ” ꡬ쑰둜 ν™•μž₯ν•˜λ©΄ 더 μž¬λ―Έμžˆμ„ 것이라 μƒκ°ν–ˆμŒ. λ§€ λΌμš΄λ“œ 승리 μ‹œ λ‹¨μˆœνžˆ μ „μ§„ν•˜λŠ” λŒ€μ‹  β€˜μ „μ§„β€™ν•˜κ±°λ‚˜ β€˜μƒλŒ€λ₯Ό λ°©ν•΄β€™ν•˜λŠ” μ„ νƒκΆŒμ„ μ£Όλ©΄ κ²Œμž„μ— 심리전이 생기며 λ‹¨μˆœ ν™•λ₯  κ²Œμž„μ΄ 리슀크 관리와 νŒλ‹¨μ΄ κ³΅μ‘΄ν•˜λŠ” ꡬ쑰둜 λ°”λ€œ. μ΄λ ‡κ²Œ ν•˜λ©΄ λ§€ 턴이 의미 μžˆλŠ” κ²°μ •μ˜ μˆœκ°„μ΄ λ˜μ–΄ 짧은 μ½˜μ†” κ²Œμž„ μ•ˆμ—μ„œλ„ μ „λž΅μ  μž¬λ―Έμ™€ λͺ°μž…감을 쀄 수 μžˆμ„ 것이라 μƒκ°ν–ˆμŒ. \ No newline at end of file diff --git a/__tests__/raceTest.js b/__tests__/raceTest.js new file mode 100644 index 00000000..45b14caf --- /dev/null +++ b/__tests__/raceTest.js @@ -0,0 +1,45 @@ +import { shouldMove, playRound, playAllRounds } from "../src/race.js"; + + +const seqRng = (...nums) => { + let i = 0; + return () => nums[i++ % nums.length]; +}; + +describe("shouldMove", () => { + test("rng κ²°κ³Όκ°€ 4 이상이면 true", () => { + expect(shouldMove(() => 4)).toBe(true); + expect(shouldMove(() => 9)).toBe(true); + }); + + test("rng κ²°κ³Όκ°€ 3 μ΄ν•˜λ©΄ false", () => { + expect(shouldMove(() => 3)).toBe(false); + expect(shouldMove(() => 0)).toBe(false); + }); +}); + +describe("playRound", () => { + test("각 차의 이동 μ—¬λΆ€κ°€ rng에 따라 κ²°μ •", () => { + const cars = [{ name: "a", distance: 0 }, { name: "b", distance: 2 }]; + + const rng = seqRng(4, 3); + const next = playRound(cars, rng); + expect(next).toEqual([ + { name: "a", distance: 1 }, + { name: "b", distance: 2 } + ]); + }); +}); + +describe("playAllRounds", () => { + test("tryCount 만큼 λΌμš΄λ“œ μ§„ν–‰", () => { + const cars = [{ name: "a", distance: 0 }, { name: "b", distance: 0 }]; + + const rng = seqRng(4, 4, 3, 9, 4, 0); + const result = playAllRounds(cars, 3, rng); + expect(result).toEqual([ + { name: "a", distance: 2 }, + { name: "b", distance: 2 } + ]); + }); +}); diff --git a/__tests__/validateTest.js b/__tests__/validateTest.js new file mode 100644 index 00000000..68f7fd3d --- /dev/null +++ b/__tests__/validateTest.js @@ -0,0 +1,37 @@ +import { validateCarNames, validateTryCount } from "../src/validate.js"; + +describe("validateCarNames", () => { + test("정상: 곡백 트림 + 5자 μ΄ν•˜", () => { + expect(validateCarNames(["pobi", " woni ", "jun"])).toBe(true); + }); + + test("μ—λŸ¬: 배열이 μ•„λ‹˜ λ˜λŠ” 빈 λ°°μ—΄", () => { + expect(() => validateCarNames(null)).toThrow("[ERROR] 잘λͺ»λœ 이름 ν˜•μ‹μž…λ‹ˆλ‹€."); + expect(() => validateCarNames(undefined)).toThrow("[ERROR] 잘λͺ»λœ 이름 ν˜•μ‹μž…λ‹ˆλ‹€."); + expect(() => validateCarNames([])).toThrow("[ERROR] 잘λͺ»λœ 이름 ν˜•μ‹μž…λ‹ˆλ‹€."); + }); + + test("μ—λŸ¬: 빈 λ¬Έμžμ—΄/곡백만", () => { + expect(() => validateCarNames(["", "pobi"])).toThrow("[ERROR] 잘λͺ»λœ 이름 ν˜•μ‹μž…λ‹ˆλ‹€."); + expect(() => validateCarNames([" "])).toThrow("[ERROR] 잘λͺ»λœ 이름 ν˜•μ‹μž…λ‹ˆλ‹€."); + }); + + test("μ—λŸ¬: 6자 이상", () => { + expect(() => validateCarNames(["longggg"])).toThrow("[ERROR] μžλ™μ°¨ 이름은 5자 μ΄ν•˜λ§Œ κ°€λŠ₯ν•©λ‹ˆλ‹€."); + }); +}); + +describe("validateTryCount", () => { + test("정상: 1 μ΄μƒμ˜ μ •μˆ˜", () => { + expect(() => validateTryCount(1)).not.toThrow(); + expect(() => validateTryCount(3)).not.toThrow(); + }); + + test("μ—λŸ¬: 1 미만/μ •μˆ˜ μ•„λ‹˜/NaN", () => { + const msg = "[ERROR] μ‹œλ„ νšŸμˆ˜λŠ” 1 μ΄μƒμ˜ μˆ«μžμ—¬μ•Ό ν•©λ‹ˆλ‹€."; + expect(() => validateTryCount(0)).toThrow(msg); + expect(() => validateTryCount(-1)).toThrow(msg); + expect(() => validateTryCount(1.5)).toThrow(msg); + expect(() => validateTryCount(NaN)).toThrow(msg); + }); +}); diff --git a/package-lock.json b/package-lock.json index 7159bf0c..c5f8d68d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "javascript-racingcar", "version": "0.0.0", + "license": "ISC", "dependencies": { "@woowacourse/mission-utils": "^2.2.0" }, @@ -14,7 +15,7 @@ "@babel/core": "^7.25.8", "@babel/preset-env": "^7.25.8", "babel-jest": "^30.1.2", - "jest": "^30.1.2" + "jest": "^30.2.0" }, "engines": { "node": ">=22.19.0", @@ -1771,9 +1772,9 @@ "license": "MIT" }, "node_modules/@emnapi/core": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz", - "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.6.0.tgz", + "integrity": "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg==", "dev": true, "license": "MIT", "optional": true, @@ -1783,9 +1784,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", - "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.6.0.tgz", + "integrity": "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA==", "dev": true, "license": "MIT", "optional": true, @@ -1850,17 +1851,17 @@ } }, "node_modules/@jest/console": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.1.2.tgz", - "integrity": "sha512-BGMAxj8VRmoD0MoA/jo9alMXSRoqW8KPeqOfEo1ncxnRLatTBCpRoOwlwlEMdudp68Q6WSGwYrrLtTGOh8fLzw==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", + "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.0.5", + "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", - "jest-message-util": "30.1.0", - "jest-util": "30.0.5", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", "slash": "^3.0.0" }, "engines": { @@ -1868,39 +1869,39 @@ } }, "node_modules/@jest/core": { - "version": "30.1.3", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.1.3.tgz", - "integrity": "sha512-LIQz7NEDDO1+eyOA2ZmkiAyYvZuo6s1UxD/e2IHldR6D7UYogVq3arTmli07MkENLq6/3JEQjp0mA8rrHHJ8KQ==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", + "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.1.2", + "@jest/console": "30.2.0", "@jest/pattern": "30.0.1", - "@jest/reporters": "30.1.3", - "@jest/test-result": "30.1.3", - "@jest/transform": "30.1.2", - "@jest/types": "30.0.5", + "@jest/reporters": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", "@types/node": "*", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", "ci-info": "^4.2.0", "exit-x": "^0.2.2", "graceful-fs": "^4.2.11", - "jest-changed-files": "30.0.5", - "jest-config": "30.1.3", - "jest-haste-map": "30.1.0", - "jest-message-util": "30.1.0", + "jest-changed-files": "30.2.0", + "jest-config": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", "jest-regex-util": "30.0.1", - "jest-resolve": "30.1.3", - "jest-resolve-dependencies": "30.1.3", - "jest-runner": "30.1.3", - "jest-runtime": "30.1.3", - "jest-snapshot": "30.1.2", - "jest-util": "30.0.5", - "jest-validate": "30.1.0", - "jest-watcher": "30.1.3", + "jest-resolve": "30.2.0", + "jest-resolve-dependencies": "30.2.0", + "jest-runner": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "jest-watcher": "30.2.0", "micromatch": "^4.0.8", - "pretty-format": "30.0.5", + "pretty-format": "30.2.0", "slash": "^3.0.0" }, "engines": { @@ -1926,39 +1927,39 @@ } }, "node_modules/@jest/environment": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.1.2.tgz", - "integrity": "sha512-N8t1Ytw4/mr9uN28OnVf0SYE2dGhaIxOVYcwsf9IInBKjvofAjbFRvedvBBlyTYk2knbJTiEjEJ2PyyDIBnd9w==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", + "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/fake-timers": "30.1.2", - "@jest/types": "30.0.5", + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", "@types/node": "*", - "jest-mock": "30.0.5" + "jest-mock": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/expect": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.1.2.tgz", - "integrity": "sha512-tyaIExOwQRCxPCGNC05lIjWJztDwk2gPDNSDGg1zitXJJ8dC3++G/CRjE5mb2wQsf89+lsgAgqxxNpDLiCViTA==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", "dev": true, "license": "MIT", "dependencies": { - "expect": "30.1.2", - "jest-snapshot": "30.1.2" + "expect": "30.2.0", + "jest-snapshot": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.1.2.tgz", - "integrity": "sha512-HXy1qT/bfdjCv7iC336ExbqqYtZvljrV8odNdso7dWK9bSeHtLlvwWWC3YSybSPL03Gg5rug6WLCZAZFH72m0A==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", + "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", "dev": true, "license": "MIT", "dependencies": { @@ -1969,18 +1970,18 @@ } }, "node_modules/@jest/fake-timers": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.1.2.tgz", - "integrity": "sha512-Beljfv9AYkr9K+ETX9tvV61rJTY706BhBUtiaepQHeEGfe0DbpvUA5Z3fomwc5Xkhns6NWrcFDZn+72fLieUnA==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", + "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.0.5", + "@jest/types": "30.2.0", "@sinonjs/fake-timers": "^13.0.0", "@types/node": "*", - "jest-message-util": "30.1.0", - "jest-mock": "30.0.5", - "jest-util": "30.0.5" + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -1997,16 +1998,16 @@ } }, "node_modules/@jest/globals": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.1.2.tgz", - "integrity": "sha512-teNTPZ8yZe3ahbYnvnVRDeOjr+3pu2uiAtNtrEsiMjVPPj+cXd5E/fr8BL7v/T7F31vYdEHrI5cC/2OoO/vM9A==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", + "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.1.2", - "@jest/expect": "30.1.2", - "@jest/types": "30.0.5", - "jest-mock": "30.0.5" + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/types": "30.2.0", + "jest-mock": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -2027,17 +2028,17 @@ } }, "node_modules/@jest/reporters": { - "version": "30.1.3", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.1.3.tgz", - "integrity": "sha512-VWEQmJWfXMOrzdFEOyGjUEOuVXllgZsoPtEHZzfdNz18RmzJ5nlR6kp8hDdY8dDS1yGOXAY7DHT+AOHIPSBV0w==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", + "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.1.2", - "@jest/test-result": "30.1.3", - "@jest/transform": "30.1.2", - "@jest/types": "30.0.5", + "@jest/console": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", "@jridgewell/trace-mapping": "^0.3.25", "@types/node": "*", "chalk": "^4.1.2", @@ -2050,9 +2051,9 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^5.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "30.1.0", - "jest-util": "30.0.5", - "jest-worker": "30.1.0", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", "slash": "^3.0.0", "string-length": "^4.0.2", "v8-to-istanbul": "^9.0.1" @@ -2083,13 +2084,13 @@ } }, "node_modules/@jest/snapshot-utils": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.1.2.tgz", - "integrity": "sha512-vHoMTpimcPSR7OxS2S0V1Cpg8eKDRxucHjoWl5u4RQcnxqQrV3avETiFpl8etn4dqxEGarBeHbIBety/f8mLXw==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", + "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.0.5", + "@jest/types": "30.2.0", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", "natural-compare": "^1.4.0" @@ -2114,14 +2115,14 @@ } }, "node_modules/@jest/test-result": { - "version": "30.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.1.3.tgz", - "integrity": "sha512-P9IV8T24D43cNRANPPokn7tZh0FAFnYS2HIfi5vK18CjRkTDR9Y3e1BoEcAJnl4ghZZF4Ecda4M/k41QkvurEQ==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", + "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.1.2", - "@jest/types": "30.0.5", + "@jest/console": "30.2.0", + "@jest/types": "30.2.0", "@types/istanbul-lib-coverage": "^2.0.6", "collect-v8-coverage": "^1.0.2" }, @@ -2130,15 +2131,15 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "30.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.1.3.tgz", - "integrity": "sha512-82J+hzC0qeQIiiZDThh+YUadvshdBswi5nuyXlEmXzrhw5ZQSRHeQ5LpVMD/xc8B3wPePvs6VMzHnntxL+4E3w==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", + "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "30.1.3", + "@jest/test-result": "30.2.0", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.1.0", + "jest-haste-map": "30.2.0", "slash": "^3.0.0" }, "engines": { @@ -2146,23 +2147,23 @@ } }, "node_modules/@jest/transform": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.1.2.tgz", - "integrity": "sha512-UYYFGifSgfjujf1Cbd3iU/IQoSd6uwsj8XHj5DSDf5ERDcWMdJOPTkHWXj4U+Z/uMagyOQZ6Vne8C4nRIrCxqA==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", + "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.27.4", - "@jest/types": "30.0.5", + "@jest/types": "30.2.0", "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.0", + "babel-plugin-istanbul": "^7.0.1", "chalk": "^4.1.2", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.1.0", + "jest-haste-map": "30.2.0", "jest-regex-util": "30.0.1", - "jest-util": "30.0.5", + "jest-util": "30.2.0", "micromatch": "^4.0.8", "pirates": "^4.0.7", "slash": "^3.0.0", @@ -2173,9 +2174,9 @@ } }, "node_modules/@jest/types": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.5.tgz", - "integrity": "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", + "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", "dev": true, "license": "MIT", "dependencies": { @@ -2777,16 +2778,16 @@ } }, "node_modules/babel-jest": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.1.2.tgz", - "integrity": "sha512-IQCus1rt9kaSh7PQxLYRY5NmkNrNlU2TpabzwV7T2jljnpdHOcmnYYv8QmE04Li4S3a2Lj8/yXyET5pBarPr6g==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz", + "integrity": "sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/transform": "30.1.2", + "@jest/transform": "30.2.0", "@types/babel__core": "^7.20.5", - "babel-plugin-istanbul": "^7.0.0", - "babel-preset-jest": "30.0.1", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.2.0", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", "slash": "^3.0.0" @@ -2795,7 +2796,7 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { - "@babel/core": "^7.11.0" + "@babel/core": "^7.11.0 || ^8.0.0-0" } }, "node_modules/babel-plugin-istanbul": { @@ -2819,14 +2820,12 @@ } }, "node_modules/babel-plugin-jest-hoist": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.0.1.tgz", - "integrity": "sha512-zTPME3pI50NsFW8ZBaVIOeAxzEY7XHlmWeXXu9srI+9kNfzCUTy8MFan46xOGZY8NZThMqq+e3qZUKsvXbasnQ==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz", + "integrity": "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", "@types/babel__core": "^7.20.5" }, "engines": { @@ -2903,20 +2902,20 @@ } }, "node_modules/babel-preset-jest": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.0.1.tgz", - "integrity": "sha512-+YHejD5iTWI46cZmcc/YtX4gaKBtdqCHCVfuVinizVpbmyjO3zYmeuyFdfA8duRqQZfgCAMlsfmkVbJ+e2MAJw==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz", + "integrity": "sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==", "dev": true, "license": "MIT", "dependencies": { - "babel-plugin-jest-hoist": "30.0.1", - "babel-preset-current-node-syntax": "^1.1.0" + "babel-plugin-jest-hoist": "30.2.0", + "babel-preset-current-node-syntax": "^1.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { - "@babel/core": "^7.11.0" + "@babel/core": "^7.11.0 || ^8.0.0-beta.1" } }, "node_modules/balanced-match": { @@ -3180,9 +3179,9 @@ } }, "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", "dev": true, "license": "MIT" }, @@ -3432,18 +3431,18 @@ } }, "node_modules/expect": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.1.2.tgz", - "integrity": "sha512-xvHszRavo28ejws8FpemjhwswGj4w/BetHIL8cU49u4sGyXDw2+p3YbeDbj6xzlxi6kWTjIRSTJ+9sNXPnF0Zg==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/expect-utils": "30.1.2", + "@jest/expect-utils": "30.2.0", "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.1.2", - "jest-message-util": "30.1.0", - "jest-mock": "30.0.5", - "jest-util": "30.0.5" + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -3886,16 +3885,16 @@ } }, "node_modules/jest": { - "version": "30.1.3", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.1.3.tgz", - "integrity": "sha512-Ry+p2+NLk6u8Agh5yVqELfUJvRfV51hhVBRIB5yZPY7mU0DGBmOuFG5GebZbMbm86cdQNK0fhJuDX8/1YorISQ==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.2.0.tgz", + "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.1.3", - "@jest/types": "30.0.5", + "@jest/core": "30.2.0", + "@jest/types": "30.2.0", "import-local": "^3.2.0", - "jest-cli": "30.1.3" + "jest-cli": "30.2.0" }, "bin": { "jest": "bin/jest.js" @@ -3913,14 +3912,14 @@ } }, "node_modules/jest-changed-files": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.0.5.tgz", - "integrity": "sha512-bGl2Ntdx0eAwXuGpdLdVYVr5YQHnSZlQ0y9HVDu565lCUAe9sj6JOtBbMmBBikGIegne9piDDIOeiLVoqTkz4A==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.2.0.tgz", + "integrity": "sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==", "dev": true, "license": "MIT", "dependencies": { "execa": "^5.1.1", - "jest-util": "30.0.5", + "jest-util": "30.2.0", "p-limit": "^3.1.0" }, "engines": { @@ -3928,29 +3927,29 @@ } }, "node_modules/jest-circus": { - "version": "30.1.3", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.1.3.tgz", - "integrity": "sha512-Yf3dnhRON2GJT4RYzM89t/EXIWNxKTpWTL9BfF3+geFetWP4XSvJjiU1vrWplOiUkmq8cHLiwuhz+XuUp9DscA==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz", + "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.1.2", - "@jest/expect": "30.1.2", - "@jest/test-result": "30.1.3", - "@jest/types": "30.0.5", + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "co": "^4.6.0", "dedent": "^1.6.0", "is-generator-fn": "^2.1.0", - "jest-each": "30.1.0", - "jest-matcher-utils": "30.1.2", - "jest-message-util": "30.1.0", - "jest-runtime": "30.1.3", - "jest-snapshot": "30.1.2", - "jest-util": "30.0.5", + "jest-each": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", "p-limit": "^3.1.0", - "pretty-format": "30.0.5", + "pretty-format": "30.2.0", "pure-rand": "^7.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.6" @@ -3960,21 +3959,21 @@ } }, "node_modules/jest-cli": { - "version": "30.1.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.1.3.tgz", - "integrity": "sha512-G8E2Ol3OKch1DEeIBl41NP7OiC6LBhfg25Btv+idcusmoUSpqUkbrneMqbW9lVpI/rCKb/uETidb7DNteheuAQ==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz", + "integrity": "sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.1.3", - "@jest/test-result": "30.1.3", - "@jest/types": "30.0.5", + "@jest/core": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", "chalk": "^4.1.2", "exit-x": "^0.2.2", "import-local": "^3.2.0", - "jest-config": "30.1.3", - "jest-util": "30.0.5", - "jest-validate": "30.1.0", + "jest-config": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", "yargs": "^17.7.2" }, "bin": { @@ -3993,34 +3992,34 @@ } }, "node_modules/jest-config": { - "version": "30.1.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.1.3.tgz", - "integrity": "sha512-M/f7gqdQEPgZNA181Myz+GXCe8jXcJsGjCMXUzRj22FIXsZOyHNte84e0exntOvdPaeh9tA0w+B8qlP2fAezfw==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz", + "integrity": "sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.27.4", "@jest/get-type": "30.1.0", "@jest/pattern": "30.0.1", - "@jest/test-sequencer": "30.1.3", - "@jest/types": "30.0.5", - "babel-jest": "30.1.2", + "@jest/test-sequencer": "30.2.0", + "@jest/types": "30.2.0", + "babel-jest": "30.2.0", "chalk": "^4.1.2", "ci-info": "^4.2.0", "deepmerge": "^4.3.1", "glob": "^10.3.10", "graceful-fs": "^4.2.11", - "jest-circus": "30.1.3", - "jest-docblock": "30.0.1", - "jest-environment-node": "30.1.2", + "jest-circus": "30.2.0", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", "jest-regex-util": "30.0.1", - "jest-resolve": "30.1.3", - "jest-runner": "30.1.3", - "jest-util": "30.0.5", - "jest-validate": "30.1.0", + "jest-resolve": "30.2.0", + "jest-runner": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", "micromatch": "^4.0.8", "parse-json": "^5.2.0", - "pretty-format": "30.0.5", + "pretty-format": "30.2.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -4045,25 +4044,25 @@ } }, "node_modules/jest-diff": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.1.2.tgz", - "integrity": "sha512-4+prq+9J61mOVXCa4Qp8ZjavdxzrWQXrI80GNxP8f4tkI2syPuPrJgdRPZRrfUTRvIoUwcmNLbqEJy9W800+NQ==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", + "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", "dev": true, "license": "MIT", "dependencies": { "@jest/diff-sequences": "30.0.1", "@jest/get-type": "30.1.0", "chalk": "^4.1.2", - "pretty-format": "30.0.5" + "pretty-format": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-docblock": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.0.1.tgz", - "integrity": "sha512-/vF78qn3DYphAaIc3jy4gA7XSAz167n9Bm/wn/1XhTLW7tTBIzXtCJpb/vcmc73NIIeeohCbdL94JasyXUZsGA==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", + "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", "dev": true, "license": "MIT", "dependencies": { @@ -4074,56 +4073,56 @@ } }, "node_modules/jest-each": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.1.0.tgz", - "integrity": "sha512-A+9FKzxPluqogNahpCv04UJvcZ9B3HamqpDNWNKDjtxVRYB8xbZLFuCr8JAJFpNp83CA0anGQFlpQna9Me+/tQ==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz", + "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==", "dev": true, "license": "MIT", "dependencies": { "@jest/get-type": "30.1.0", - "@jest/types": "30.0.5", + "@jest/types": "30.2.0", "chalk": "^4.1.2", - "jest-util": "30.0.5", - "pretty-format": "30.0.5" + "jest-util": "30.2.0", + "pretty-format": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-environment-node": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.1.2.tgz", - "integrity": "sha512-w8qBiXtqGWJ9xpJIA98M0EIoq079GOQRQUyse5qg1plShUCQ0Ek1VTTcczqKrn3f24TFAgFtT+4q3aOXvjbsuA==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", + "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.1.2", - "@jest/fake-timers": "30.1.2", - "@jest/types": "30.0.5", + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", "@types/node": "*", - "jest-mock": "30.0.5", - "jest-util": "30.0.5", - "jest-validate": "30.1.0" + "jest-mock": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-haste-map": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.1.0.tgz", - "integrity": "sha512-JLeM84kNjpRkggcGpQLsV7B8W4LNUWz7oDNVnY1Vjj22b5/fAb3kk3htiD+4Na8bmJmjJR7rBtS2Rmq/NEcADg==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz", + "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.0.5", + "@jest/types": "30.2.0", "@types/node": "*", "anymatch": "^3.1.3", "fb-watchman": "^2.0.2", "graceful-fs": "^4.2.11", "jest-regex-util": "30.0.1", - "jest-util": "30.0.5", - "jest-worker": "30.1.0", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", "micromatch": "^4.0.8", "walker": "^1.0.8" }, @@ -4135,49 +4134,49 @@ } }, "node_modules/jest-leak-detector": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.1.0.tgz", - "integrity": "sha512-AoFvJzwxK+4KohH60vRuHaqXfWmeBATFZpzpmzNmYTtmRMiyGPVhkXpBqxUQunw+dQB48bDf4NpUs6ivVbRv1g==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz", + "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==", "dev": true, "license": "MIT", "dependencies": { "@jest/get-type": "30.1.0", - "pretty-format": "30.0.5" + "pretty-format": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.1.2.tgz", - "integrity": "sha512-7ai16hy4rSbDjvPTuUhuV8nyPBd6EX34HkBsBcBX2lENCuAQ0qKCPb/+lt8OSWUa9WWmGYLy41PrEzkwRwoGZQ==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", + "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", "dev": true, "license": "MIT", "dependencies": { "@jest/get-type": "30.1.0", "chalk": "^4.1.2", - "jest-diff": "30.1.2", - "pretty-format": "30.0.5" + "jest-diff": "30.2.0", + "pretty-format": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-message-util": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.1.0.tgz", - "integrity": "sha512-HizKDGG98cYkWmaLUHChq4iN+oCENohQLb7Z5guBPumYs+/etonmNFlg1Ps6yN9LTPyZn+M+b/9BbnHx3WTMDg==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", + "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@jest/types": "30.0.5", + "@jest/types": "30.2.0", "@types/stack-utils": "^2.0.3", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", "micromatch": "^4.0.8", - "pretty-format": "30.0.5", + "pretty-format": "30.2.0", "slash": "^3.0.0", "stack-utils": "^2.0.6" }, @@ -4186,15 +4185,15 @@ } }, "node_modules/jest-mock": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.5.tgz", - "integrity": "sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", + "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.0.5", + "@jest/types": "30.2.0", "@types/node": "*", - "jest-util": "30.0.5" + "jest-util": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -4229,18 +4228,18 @@ } }, "node_modules/jest-resolve": { - "version": "30.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.1.3.tgz", - "integrity": "sha512-DI4PtTqzw9GwELFS41sdMK32Ajp3XZQ8iygeDMWkxlRhm7uUTOFSZFVZABFuxr0jvspn8MAYy54NxZCsuCTSOw==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz", + "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.2", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.1.0", + "jest-haste-map": "30.2.0", "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.0.5", - "jest-validate": "30.1.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", "slash": "^3.0.0", "unrs-resolver": "^1.7.11" }, @@ -4249,46 +4248,46 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "30.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.1.3.tgz", - "integrity": "sha512-DNfq3WGmuRyHRHfEet+Zm3QOmVFtIarUOQHHryKPc0YL9ROfgWZxl4+aZq/VAzok2SS3gZdniP+dO4zgo59hBg==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz", + "integrity": "sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==", "dev": true, "license": "MIT", "dependencies": { "jest-regex-util": "30.0.1", - "jest-snapshot": "30.1.2" + "jest-snapshot": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-runner": { - "version": "30.1.3", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.1.3.tgz", - "integrity": "sha512-dd1ORcxQraW44Uz029TtXj85W11yvLpDuIzNOlofrC8GN+SgDlgY4BvyxJiVeuabA1t6idjNbX59jLd2oplOGQ==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz", + "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.1.2", - "@jest/environment": "30.1.2", - "@jest/test-result": "30.1.3", - "@jest/transform": "30.1.2", - "@jest/types": "30.0.5", + "@jest/console": "30.2.0", + "@jest/environment": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "emittery": "^0.13.1", "exit-x": "^0.2.2", "graceful-fs": "^4.2.11", - "jest-docblock": "30.0.1", - "jest-environment-node": "30.1.2", - "jest-haste-map": "30.1.0", - "jest-leak-detector": "30.1.0", - "jest-message-util": "30.1.0", - "jest-resolve": "30.1.3", - "jest-runtime": "30.1.3", - "jest-util": "30.0.5", - "jest-watcher": "30.1.3", - "jest-worker": "30.1.0", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-leak-detector": "30.2.0", + "jest-message-util": "30.2.0", + "jest-resolve": "30.2.0", + "jest-runtime": "30.2.0", + "jest-util": "30.2.0", + "jest-watcher": "30.2.0", + "jest-worker": "30.2.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -4297,32 +4296,32 @@ } }, "node_modules/jest-runtime": { - "version": "30.1.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.1.3.tgz", - "integrity": "sha512-WS8xgjuNSphdIGnleQcJ3AKE4tBKOVP+tKhCD0u+Tb2sBmsU8DxfbBpZX7//+XOz81zVs4eFpJQwBNji2Y07DA==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz", + "integrity": "sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.1.2", - "@jest/fake-timers": "30.1.2", - "@jest/globals": "30.1.2", + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/globals": "30.2.0", "@jest/source-map": "30.0.1", - "@jest/test-result": "30.1.3", - "@jest/transform": "30.1.2", - "@jest/types": "30.0.5", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "cjs-module-lexer": "^2.1.0", "collect-v8-coverage": "^1.0.2", "glob": "^10.3.10", "graceful-fs": "^4.2.11", - "jest-haste-map": "30.1.0", - "jest-message-util": "30.1.0", - "jest-mock": "30.0.5", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", "jest-regex-util": "30.0.1", - "jest-resolve": "30.1.3", - "jest-snapshot": "30.1.2", - "jest-util": "30.0.5", + "jest-resolve": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -4331,9 +4330,9 @@ } }, "node_modules/jest-snapshot": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.1.2.tgz", - "integrity": "sha512-4q4+6+1c8B6Cy5pGgFvjDy/Pa6VYRiGu0yQafKkJ9u6wQx4G5PqI2QR6nxTl43yy7IWsINwz6oT4o6tD12a8Dg==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.2.0.tgz", + "integrity": "sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==", "dev": true, "license": "MIT", "dependencies": { @@ -4342,20 +4341,20 @@ "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/types": "^7.27.3", - "@jest/expect-utils": "30.1.2", + "@jest/expect-utils": "30.2.0", "@jest/get-type": "30.1.0", - "@jest/snapshot-utils": "30.1.2", - "@jest/transform": "30.1.2", - "@jest/types": "30.0.5", - "babel-preset-current-node-syntax": "^1.1.0", + "@jest/snapshot-utils": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "babel-preset-current-node-syntax": "^1.2.0", "chalk": "^4.1.2", - "expect": "30.1.2", + "expect": "30.2.0", "graceful-fs": "^4.2.11", - "jest-diff": "30.1.2", - "jest-matcher-utils": "30.1.2", - "jest-message-util": "30.1.0", - "jest-util": "30.0.5", - "pretty-format": "30.0.5", + "jest-diff": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "pretty-format": "30.2.0", "semver": "^7.7.2", "synckit": "^0.11.8" }, @@ -4364,9 +4363,9 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { @@ -4377,13 +4376,13 @@ } }, "node_modules/jest-util": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.5.tgz", - "integrity": "sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", + "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.0.5", + "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", @@ -4408,18 +4407,18 @@ } }, "node_modules/jest-validate": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.1.0.tgz", - "integrity": "sha512-7P3ZlCFW/vhfQ8pE7zW6Oi4EzvuB4sgR72Q1INfW9m0FGo0GADYlPwIkf4CyPq7wq85g+kPMtPOHNAdWHeBOaA==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.2.0.tgz", + "integrity": "sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==", "dev": true, "license": "MIT", "dependencies": { "@jest/get-type": "30.1.0", - "@jest/types": "30.0.5", + "@jest/types": "30.2.0", "camelcase": "^6.3.0", "chalk": "^4.1.2", "leven": "^3.1.0", - "pretty-format": "30.0.5" + "pretty-format": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -4439,19 +4438,19 @@ } }, "node_modules/jest-watcher": { - "version": "30.1.3", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.1.3.tgz", - "integrity": "sha512-6jQUZCP1BTL2gvG9E4YF06Ytq4yMb4If6YoQGRR6PpjtqOXSP3sKe2kqwB6SQ+H9DezOfZaSLnmka1NtGm3fCQ==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.2.0.tgz", + "integrity": "sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "30.1.3", - "@jest/types": "30.0.5", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", "@types/node": "*", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", "emittery": "^0.13.1", - "jest-util": "30.0.5", + "jest-util": "30.2.0", "string-length": "^4.0.2" }, "engines": { @@ -4459,15 +4458,15 @@ } }, "node_modules/jest-worker": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.1.0.tgz", - "integrity": "sha512-uvWcSjlwAAgIu133Tt77A05H7RIk3Ho8tZL50bQM2AkvLdluw9NG48lRCl3Dt+MOH719n/0nnb5YxUwcuJiKRA==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", + "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.0.5", + "jest-util": "30.2.0", "merge-stream": "^2.0.0", "supports-color": "^8.1.1" }, @@ -4609,9 +4608,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { @@ -4696,9 +4695,9 @@ "license": "MIT" }, "node_modules/napi-postinstall": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz", - "integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", "dev": true, "license": "MIT", "bin": { @@ -4967,9 +4966,9 @@ } }, "node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", + "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 6c3f2ba8..5d6b671c 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "@babel/core": "^7.25.8", "@babel/preset-env": "^7.25.8", "babel-jest": "^30.1.2", - "jest": "^30.1.2" + "jest": "^30.2.0" }, "dependencies": { "@woowacourse/mission-utils": "^2.2.0" @@ -29,5 +29,10 @@ "engines": { "npm": ">=10.8.2", "node": ">=22.19.0" - } + }, + "description": "μš°ν…Œμ½” μžλ™μ°¨ κ²½μ£Ό κ²Œμž„ 과제용 ν”„λ‘œμ νŠΈ", + "main": "index.js", + "keywords": [], + "author": "", + "license": "ISC" } diff --git a/src/App.js b/src/App.js index 091aa0a5..267964c3 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,33 @@ -class App { - async run() {} +import { getCarNames, getTryCount } from "./input.js"; +import { validateCarNames, validateTryCount } from "./validate.js"; +import { playAllRounds } from "./race.js"; +import { printExecutionHeader, printRoundResult, printWinners } from "./output.js"; +import { Console } from "@woowacourse/mission-utils"; + +export class App { + async run() { + try { + const carNames = await getCarNames(); + validateCarNames(carNames); + + const tryCount = await getTryCount(); + validateTryCount(tryCount); + + let cars = carNames.map((name) => ({ name, distance: 0 })); + + printExecutionHeader(); + + for (let i = 0; i < tryCount; i++) { + cars = playAllRounds(cars, 1); + printRoundResult(cars); + } + + printWinners(cars); + } catch (error) { + Console.print(error.message); + throw error; + } + } } export default App; diff --git a/src/input.js b/src/input.js new file mode 100644 index 00000000..b029baa4 --- /dev/null +++ b/src/input.js @@ -0,0 +1,16 @@ +import { Console } from "@woowacourse/mission-utils"; + + +export const getCarNames = async () => { + const input = await Console.readLineAsync( + "κ²½μ£Όν•  μžλ™μ°¨ 이름을 μž…λ ₯ν•˜μ„Έμš”.(이름은 μ‰Όν‘œ(,) κΈ°μ€€μœΌλ‘œ ꡬ뢄)\n" + ); + + return input.split(",").map((name)=>name.trim()); +}; + + export const getTryCount = async () => { + const input = await Console.readLineAsync("μ‹œλ„ν•  νšŸμˆ˜λŠ” λͺ‡ νšŒμΈκ°€μš”?\n"); + return Number(input); +}; + diff --git a/src/output.js b/src/output.js new file mode 100644 index 00000000..2a0eae1b --- /dev/null +++ b/src/output.js @@ -0,0 +1,20 @@ +import { Console } from "@woowacourse/mission-utils"; + +export const printExecutionHeader = () => { + Console.print("μ‹€ν–‰ κ²°κ³Ό"); +} + +export const formatCarLine = (car) => { + return `${car.name} : ${"-".repeat(car.distance)}` +}; + +export const printRoundResult = (cars) => { + cars.forEach((car)=> Console.print(formatCarLine(car))); + Console.print(""); +}; + +export const printWinners = (cars) => { + const max = Math.max(...cars.map((c)=> c.distance)); + const winners = cars.filter((c)=> c.distance === max).map((c)=>c.name); + Console.print(`μ΅œμ’… 우승자 : ${winners.join(", ")}`); +}; \ No newline at end of file diff --git a/src/race.js b/src/race.js new file mode 100644 index 00000000..c6f35abe --- /dev/null +++ b/src/race.js @@ -0,0 +1,24 @@ +import { Random } from "@woowacourse/mission-utils"; + +export const shouldMove = (rng = () => Random.pickNumberInRange(0, 9)) => { + return rng() >= 4; +}; + +export const playRound = (cars, rng = () => Random.pickNumberInRange(0, 9)) => { + return cars.map((car) => { + if (shouldMove(rng)) { + return { ...car, distance: car.distance + 1 }; + } + return { ...car }; + }); +}; + + + +export const playAllRounds = (cars, tryCount, rng = () => Random.pickNumberInRange(0, 9)) => { + let state = cars; + for (let i = 0; i < tryCount; i += 1) { + state = playRound(state, rng); + } + return state; +}; \ No newline at end of file diff --git a/src/validate.js b/src/validate.js new file mode 100644 index 00000000..193fef71 --- /dev/null +++ b/src/validate.js @@ -0,0 +1,38 @@ +const ERROR ={ + NAME_FORMAT: "[ERROR] 잘λͺ»λœ 이름 ν˜•μ‹μž…λ‹ˆλ‹€.", + NAME_LENGTH: "[ERROR] μžλ™μ°¨ 이름은 5자 μ΄ν•˜λ§Œ κ°€λŠ₯ν•©λ‹ˆλ‹€.", + TRY_COUNT: "[ERROR] μ‹œλ„ νšŸμˆ˜λŠ” 1 μ΄μƒμ˜ μˆ«μžμ—¬μ•Ό ν•©λ‹ˆλ‹€." +} + +export const validateCarNames = (names) => { + if (!Array.isArray(names) || names.length == 0) { + throw new Error (ERROR.NAME_FORMAT); + } + + + for (const raw of names) { + let name = raw; + if (name === undefined || name === null) { + name = ""; + } + name = name.trim(); + + + if (name.length===0) { + throw new Error(ERROR.NAME_FORMAT); + } + + if (name.length > 5) { + throw new Error(ERROR.NAME_LENGTH); + } + + } + + return true; +}; + +export const validateTryCount = (count) => { + if (!Number.isInteger(count)||count <1){ + throw new Error(ERROR.TRY_COUNT); + } +} \ No newline at end of file