Skip to content

Commit 3cedc42

Browse files
feature: Added new problem challenges from HackerEarth (#4)
* core(hackerearth): Added implementation of JumpGame, ParenthesesOps, and ListNodeOps with passing test cases * core(maven): Updated phase for javadocs generation * docs: Updated with new coding assignment platform [no ci] * core(devops): Added Code Coverage Gate for CI
1 parent 8c615b4 commit 3cedc42

File tree

10 files changed

+844
-5
lines changed

10 files changed

+844
-5
lines changed

.github/workflows/ci.yml

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ on:
1616
branches: [ 'main', 'develop', 'feature/**', 'hotfix/**', 'bugfix/**' ]
1717

1818
permissions:
19-
contents: read
20-
packages: read
19+
contents: write
20+
packages: write
2121
statuses: write # To report GitHub Actions status checks
22-
actions: read # Needed for detection of GitHub Actions environment.
22+
actions: write # Needed for detection of GitHub Actions environment.
2323
id-token: write # Needed for provenance signing and ID.
2424
pull-requests: write
2525

@@ -73,4 +73,61 @@ jobs:
7373
# if: steps.pmd.outputs.violations == 0
7474
# uses: github/codeql-action/upload-sarif@v3
7575
# with:
76-
# sarif_file: pmd-report.sarif
76+
# sarif_file: pmd-report.sarif
77+
coverage:
78+
name: JaCoCo Code Coverage Gate
79+
runs-on: ubuntu-latest
80+
permissions: write-all
81+
steps:
82+
- name: Checkout sources
83+
uses: actions/checkout@v5
84+
with:
85+
clean: 'true'
86+
87+
- name: Set up JDK 21
88+
uses: actions/setup-java@v5
89+
with:
90+
distribution: 'temurin'
91+
java-version: '21'
92+
cache: maven
93+
architecture: x64
94+
95+
- name: Generate Coverage Report
96+
run: |
97+
mvn -B clean install test --file pom.xml
98+
99+
# - name: Upload Report
100+
# uses: actions/upload-artifact@v3
101+
# with:
102+
# name: jacoco-report
103+
# path: ${{ github.workspace }}/target/site/jacoco/jacoco.xml
104+
105+
- name: Add coverage to PR
106+
id: jacoco
107+
uses: madrapps/[email protected]
108+
with:
109+
paths: ${{ github.workspace }}/target/site/jacoco/jacoco.xml
110+
token: ${{ secrets.GITHUB_TOKEN }}
111+
min-coverage-overall: 80
112+
min-coverage-changed-files: 80
113+
title: Code Coverage
114+
comment-type: 'pr_comment'
115+
116+
- name: Save Coverage To Environment Variable
117+
run: |
118+
echo "TOTAL_COVERAGE=${{ steps.jacoco.outputs.coverage-overall }}" >> $GITHUB_ENV
119+
echo "CHANGED_FILES_COVERAGE=${{ steps.jacoco.outputs.coverage-changed-files }}" >> $GITHUB_ENV
120+
121+
- name: Print & Check Coverage Info
122+
run: |
123+
import os
124+
import sys
125+
total_coverage = os.environ["TOTAL_COVERAGE"]
126+
changed_files_coverage = os.environ["CHANGED_FILES_COVERAGE"]
127+
print(f"Total Coverage: {total_coverage}")
128+
print(f"Changed Files Coverage: {changed_files_coverage}")
129+
if float(total_coverage) < 80 or float(changed_files_coverage) < 80:
130+
print("Insufficient Coverage!")
131+
sys.exit(-1) # Cause Status Check Failure due to noncompliant coverage
132+
sys.exit(0)
133+
shell: python

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ PrimeKit Coding Challenges
88
- [LeetCode](https://leetcode.com)
99
- [HackerRank](https://hackerrank.com)
1010
- [Codility](https://app.codility.com)
11+
- [HackerEarth](https://www.hackerearth.com/)
1112

1213
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/shortthirdman/PrimeKit-Challenges) ![GitHub repo size](https://img.shields.io/github/repo-size/shortthirdman/PrimeKit-Challenges)
1314

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@
380380
<executions>
381381
<execution>
382382
<id>attach-javadocs</id>
383-
<phase>package</phase>
383+
<phase>install</phase>
384384
<goals>
385385
<goal>jar</goal>
386386
<goal>javadoc</goal>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.shortthirdman.primekit.common;
2+
3+
public class ListNode {
4+
5+
public int value;
6+
public ListNode next;
7+
8+
public ListNode() {
9+
}
10+
11+
public ListNode(int value) {
12+
this.value = value;
13+
}
14+
15+
public ListNode(int value, ListNode next) {
16+
this.value = value;
17+
this.next = next;
18+
}
19+
20+
// Convenience method for testing
21+
public static ListNode of(int... values) {
22+
ListNode dummy = new ListNode(0);
23+
ListNode current = dummy;
24+
for (int v : values) {
25+
current.next = new ListNode(v);
26+
current = current.next;
27+
}
28+
return dummy.next;
29+
}
30+
31+
@Override
32+
public String toString() {
33+
StringBuilder sb = new StringBuilder();
34+
ListNode curr = this;
35+
while (curr != null) {
36+
sb.append(curr.value);
37+
if (curr.next != null) sb.append("->");
38+
curr = curr.next;
39+
}
40+
return sb.toString();
41+
}
42+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.shortthirdman.primekit.hackerearth;
2+
3+
import java.util.stream.IntStream;
4+
5+
/**
6+
* @author ShortThirdMan
7+
* @since 1.1.0
8+
*/
9+
public class JumpGame {
10+
11+
/**
12+
* Calculates the minimum number of jumps required to reach the last index from index 0.
13+
* <p>
14+
* Each element in the array represents the maximum jump length from that position.
15+
* Uses a greedy approach by expanding the current jump range and counting jumps
16+
* whenever the current range is exhausted.
17+
* </p>
18+
*
19+
* @param nums an integer array where each element represents maximum jump length from that index
20+
* @return the minimum number of jumps needed to reach the last index
21+
* @throws IllegalArgumentException if:
22+
* <ul>
23+
* <li>{@code nums} is {@code null}</li>
24+
* <li>length of {@code nums} is not in [1, 10^4]</li>
25+
* <li>any element in {@code nums} is not in [0, 1000]</li>
26+
* </ul>
27+
*/
28+
public int jump(int[] nums) {
29+
// Constraint checks
30+
if (nums == null || nums.length < 1 || nums.length > 10_000) {
31+
throw new IllegalArgumentException("Array length must be between 1 and 10^4");
32+
}
33+
34+
boolean invalidValue = IntStream.of(nums)
35+
.anyMatch(num -> num < 0 || num > 1_000);
36+
if (invalidValue) {
37+
throw new IllegalArgumentException("Array elements must be between 0 and 1000");
38+
}
39+
40+
if (nums.length == 1) return 0; // already at last index
41+
42+
int jumps = 0;
43+
int currentEnd = 0; // boundary of the current jump
44+
int farthest = 0; // farthest index we can reach
45+
46+
for (int i = 0; i < nums.length - 1; i++) {
47+
farthest = Math.max(farthest, i + nums[i]);
48+
49+
if (i == currentEnd) { // time to make another jump
50+
jumps++;
51+
currentEnd = farthest;
52+
}
53+
}
54+
55+
return jumps;
56+
}
57+
58+
/**
59+
* Determines if it is possible to reach the last index of the array starting from index 0.
60+
* <p>
61+
* Each element in the array represents the maximum jump length from that position.
62+
* Uses a greedy approach to keep track of the furthest reachable index at every step.
63+
* </p>
64+
*
65+
* @param nums an integer array where each element represents maximum jump length from that index
66+
* @return {@code true} if it is possible to reach the last index, otherwise {@code false}
67+
* @throws IllegalArgumentException if:
68+
* <ul>
69+
* <li>{@code nums} is {@code null}</li>
70+
* <li>length of {@code nums} is not in [1, 10^4]</li>
71+
* <li>any element in {@code nums} is not in [0, 10^5]</li>
72+
* </ul>
73+
*/
74+
public boolean canJump(int[] nums) {
75+
if (nums == null || nums.length < 1 || nums.length > 10_000) {
76+
throw new IllegalArgumentException("Array length must be between 1 and 10^4");
77+
}
78+
79+
boolean invalidValue = IntStream.of(nums)
80+
.anyMatch(num -> num < 0 || num > 100_000);
81+
if (invalidValue) {
82+
throw new IllegalArgumentException("Array elements must be between 0 and 10^5");
83+
}
84+
85+
// If there's only one element, we are already at the last index
86+
if (nums.length == 1) return true;
87+
88+
int maxReach = 0;
89+
90+
for (int i = 0; i < nums.length && i <= maxReach; i++) {
91+
maxReach = Math.max(maxReach, i + nums[i]);
92+
if (maxReach >= nums.length - 1) {
93+
return true;
94+
}
95+
}
96+
97+
return false;
98+
}
99+
}

0 commit comments

Comments
 (0)