Yacht in Cobol: Complete Solution & Deep Dive Guide
The Ultimate Guide to Mastering Logic Games in Cobol: The Yacht Score Challenge
Learn to solve the classic Yacht dice game by implementing a robust scoring calculator in Cobol. This guide covers everything from data structures and conditional logic to building a complete, callable subprogram for versatile game development, demonstrating Cobol's power for complex rule-based applications.
You’ve probably heard the whispers. "Cobol is a relic," they say. "A language of dinosaurs, confined to the basements of banks and insurance companies." It’s easy to dismiss it as a language incapable of anything... fun. But what if we told you that the very structure and discipline that make Cobol a titan of enterprise systems also make it a surprisingly elegant tool for building complex, rule-based logic engines, like those found in games?
Imagine the challenge: you're given five dice, a set of scoring rules as intricate as any business policy, and you need to build a calculator that is not just accurate, but unshakably reliable. This is where Cobol shines. In this deep dive, we'll dismantle the Yacht dice game, a classic test of logic and probability, and reconstruct it piece by piece in Cobol. You'll not only solve a fun puzzle but also gain a profound appreciation for the clarity, structure, and raw logical power of a language that has powered critical systems for decades.
What is the Yacht Scoring Challenge?
Before we dive into the code, we must first understand the rules of the game. The Yacht challenge, a core module in the kodikra.com learning curriculum, is based on a popular dice game. The objective is simple: given a set of five dice, each with faces numbered 1 through 6, and a specific scoring category, you must calculate the score according to a predefined set of rules.
This task emulates real-world business logic programming. You receive a structured set of inputs (the dice and a category) and must apply a specific rule from a "rulebook" to produce a single, correct output (the score). The complexity lies in the variety and specificity of these rules.
The Scoring Categories Explained
The game features several distinct categories, each with its own unique scoring mechanism. Understanding these is critical to building our Cobol logic. Let's break them down in detail.
| Category | Score Calculation | Description | Example Dice Roll | Resulting Score |
|---|---|---|---|---|
| Ones | Sum of all dice showing '1' | The total of all the ones. | 1, 1, 2, 4, 5 | 2 |
| Twos | Sum of all dice showing '2' | The total of all the twos. | 2, 3, 2, 5, 2 | 6 |
| Threes | Sum of all dice showing '3' | The total of all the threes. | 3, 3, 3, 4, 5 | 9 |
| Fours | Sum of all dice showing '4' | The total of all the fours. | 1, 1, 2, 4, 4 | 8 |
| Fives | Sum of all dice showing '5' | The total of all the fives. | 5, 1, 5, 2, 5 | 15 |
| Sixes | Sum of all dice showing '6' | The total of all the sixes. | 2, 3, 4, 6, 6 | 12 |
| Full House | Sum of all dice | Three of one number and two of another. | 3, 3, 3, 5, 5 | 19 |
| Four of a Kind | Sum of the four matching dice | At least four dice showing the same number. | 4, 4, 4, 4, 6 | 16 |
| Little Straight | 30 points | Dice show 1, 2, 3, 4, 5. | 1, 2, 3, 4, 5 | 30 |
| Big Straight | 30 points | Dice show 2, 3, 4, 5, 6. | 2, 3, 4, 5, 6 | 30 |
| Choice | Sum of all dice | A "catch-all" category. Any combination. | 1, 3, 4, 5, 6 | 19 |
| Yacht | 50 points | All five dice are the same number. | 4, 4, 4, 4, 4 | 50 |
Why Use Cobol for a Logic-Based Game?
Choosing Cobol for a new project, especially one that sounds like a game, might seem counterintuitive in an era dominated by Python, JavaScript, and Go. However, this choice is deliberate and highlights the unique strengths of Cobol that are often overlooked.
Firstly, Cobol's verbosity is a feature, not a bug. Its English-like syntax makes the program's logic exceptionally clear and self-documenting. When you're implementing a dozen different scoring rules, this clarity prevents bugs and makes the code easier to maintain and debug. A line like ADD WS-DIE-VALUE TO WS-SCORE leaves no room for ambiguity.
Secondly, Cobol's rigid data structure, defined in the DATA DIVISION, forces the programmer to think carefully about their data upfront. This discipline helps in creating a clean, organized, and efficient program. Defining tables (arrays) for dice and their counts provides a structured way to analyze the input, which is essential for categories like "Full House" or "Four of a Kind".
Finally, Cobol is built for reliability and batch processing. While our Yacht calculator is a small-scale example, the underlying principles are the same as those used in processing millions of financial transactions. The program is designed to take a set of inputs, execute a series of deterministic rules, and produce a correct output, every single time. This makes it an excellent teaching tool for writing robust, error-resistant code.
● Start Program
│
▼
┌───────────────────┐
│ Receive Inputs: │
│ - 5 Dice Values │
│ - Category Name │
└─────────┬─────────┘
│
▼
┌───────────────────┐
│ Count Frequencies │
│ of each Die Value │
│ (1s, 2s, 3s...) │
└─────────┬─────────┘
│
▼
◆ EVALUATE Category
╱ │ │ ╲
"Ones" "Full.." "Yacht" "Other"
│ │ │ │
▼ ▼ ▼ ▼
[Calc [Calc [Calc [Set
Sum] House] Yacht] Score 0]
│ │ │ │
└──────┼──────┼────────┘
│
▼
┌───────────────────┐
│ Return WS-SCORE │
└─────────┬─────────┘
│
▼
● End Program
How to Structure and Implement the Cobol Solution
A well-structured Cobol program is a thing of beauty. It's organized into distinct divisions, each with a clear purpose. Our Yacht scorer will follow this classic structure to ensure clarity and maintainability.
The Four Divisions of a Cobol Program
- IDENTIFICATION DIVISION: This is the metadata section. It contains the
PROGRAM-IDand other optional information like the author and date. - ENVIRONMENT DIVISION: Used to specify the computer environment for compiling and running the program. For our simple case, this will be minimal.
- DATA DIVISION: The heart of our data definition. Here, we'll declare all our variables, constants, and data structures (tables) needed to store the dice, their counts, and the final score.
- PROCEDURE DIVISION: This is where the logic lives. It contains the instructions, paragraphs (functions), and control structures (loops, conditionals) that will execute our scoring rules.
The Complete Cobol Source Code
Here is the complete, well-commented source code for the Yacht scoring program. We've designed it to be a callable subprogram, a common practice in enterprise Cobol development, which makes it reusable. The main logic is handled by a LINKAGE SECTION to accept parameters.
IDENTIFICATION DIVISION.
PROGRAM-ID. YACHT-SCORER.
AUTHOR. Kodikra.
*
* This program calculates the score for a game of Yacht given
* a set of five dice and a scoring category.
* It is designed as a callable subprogram.
*
DATA DIVISION.
WORKING-STORAGE SECTION.
* --- Internal Variables and Loop Counters
01 WS-COUNTERS.
05 I PIC 9(1).
05 J PIC 9(1).
* --- Data Structures for Dice Analysis
01 WS-DICE-ANALYSIS.
05 WS-DICE-COUNTS PIC 9(1) OCCURS 6 TIMES.
05 WS-HAS-PAIR PIC X(1) VALUE 'N'.
05 WS-HAS-TRIPLE PIC X(1) VALUE 'N'.
05 WS-FOUR-KIND-VAL PIC 9(1) VALUE 0.
* --- Intermediate Calculation Variables
01 WS-CALCULATION-VARS.
05 WS-SUM PIC 9(2) VALUE 0.
LINKAGE SECTION.
* --- Input Parameters from Calling Program
01 LS-DICE-INPUT.
05 LS-DICE PIC 9(1) OCCURS 5 TIMES.
01 LS-CATEGORY PIC X(20).
* --- Output Parameter
01 LS-SCORE PIC 9(2).
*
PROCEDURE DIVISION USING LS-DICE-INPUT, LS-CATEGORY, LS-SCORE.
*================================================================*
* MAIN-LOGIC *
*================================================================*
MAIN-LOGIC.
INITIALIZE WS-COUNTERS WS-DICE-ANALYSIS WS-CALCULATION-VARS.
MOVE 0 TO LS-SCORE.
PERFORM COUNT-DICE-FREQUENCIES.
EVALUATE FUNCTION UPPER-CASE(LS-CATEGORY)
WHEN "ONES"
PERFORM CALCULATE-NUMBERED-CATEGORY VARYING I FROM 1 BY 1 UNTIL I > 1
WHEN "TWOS"
PERFORM CALCULATE-NUMBERED-CATEGORY VARYING I FROM 2 BY 1 UNTIL I > 2
WHEN "THREES"
PERFORM CALCULATE-NUMBERED-CATEGORY VARYING I FROM 3 BY 1 UNTIL I > 3
WHEN "FOURS"
PERFORM CALCULATE-NUMBERED-CATEGORY VARYING I FROM 4 BY 1 UNTIL I > 4
WHEN "FIVES"
PERFORM CALCULATE-NUMBERED-CATEGORY VARYING I FROM 5 BY 1 UNTIL I > 5
WHEN "SIXES"
PERFORM CALCULATE-NUMBERED-CATEGORY VARYING I FROM 6 BY 1 UNTIL I > 6
WHEN "FULL HOUSE"
PERFORM CALCULATE-FULL-HOUSE
WHEN "FOUR OF A KIND"
PERFORM CALCULATE-FOUR-OF-A-KIND
WHEN "LITTLE STRAIGHT"
PERFORM CALCULATE-LITTLE-STRAIGHT
WHEN "BIG STRAIGHT"
PERFORM CALCULATE-BIG-STRAIGHT
WHEN "CHOICE"
PERFORM CALCULATE-CHOICE
WHEN "YACHT"
PERFORM CALCULATE-YACHT
END-EVALUATE.
GOBACK.
*
*================================================================*
* HELPER-PARAGRAPHS *
*================================================================*
COUNT-DICE-FREQUENCIES.
* Counts how many of each die value (1-6) are present.
PERFORM VARYING I FROM 1 BY 1 UNTIL I > 5
ADD 1 TO WS-DICE-COUNTS(LS-DICE(I))
END-PERFORM.
*
CALCULATE-NUMBERED-CATEGORY.
* Calculates score for categories "Ones" through "Sixes".
* The calling PERFORM statement sets 'I' to the target number.
COMPUTE LS-SCORE = WS-DICE-COUNTS(I) * I.
*
CALCULATE-CHOICE.
* Calculates the sum of all dice.
PERFORM VARYING I FROM 1 BY 1 UNTIL I > 5
ADD LS-DICE(I) TO LS-SCORE
END-PERFORM.
*
CALCULATE-YACHT.
* Checks if all five dice are the same.
PERFORM VARYING I FROM 1 BY 1 UNTIL I > 6
IF WS-DICE-COUNTS(I) = 5
MOVE 50 TO LS-SCORE
END-IF
END-PERFORM.
*
CALCULATE-FULL-HOUSE.
* Checks for a pair and a triple.
PERFORM VARYING I FROM 1 BY 1 UNTIL I > 6
IF WS-DICE-COUNTS(I) = 2
MOVE 'Y' TO WS-HAS-PAIR
END-IF
IF WS-DICE-COUNTS(I) = 3
MOVE 'Y' TO WS-HAS-TRIPLE
END-IF
END-PERFORM.
IF WS-HAS-PAIR = 'Y' AND WS-HAS-TRIPLE = 'Y'
PERFORM CALCULATE-CHOICE
END-IF.
*
CALCULATE-FOUR-OF-A-KIND.
* Checks for four or five of the same die.
PERFORM VARYING I FROM 1 BY 1 UNTIL I > 6
IF WS-DICE-COUNTS(I) >= 4
MOVE I TO WS-FOUR-KIND-VAL
END-IF
END-PERFORM.
IF WS-FOUR-KIND-VAL > 0
COMPUTE LS-SCORE = WS-FOUR-KIND-VAL * 4
END-IF.
*
CALCULATE-LITTLE-STRAIGHT.
* Checks for dice 1, 2, 3, 4, 5.
IF WS-DICE-COUNTS(1) = 1 AND
WS-DICE-COUNTS(2) = 1 AND
WS-DICE-COUNTS(3) = 1 AND
WS-DICE-COUNTS(4) = 1 AND
WS-DICE-COUNTS(5) = 1
MOVE 30 TO LS-SCORE
END-IF.
*
CALCULATE-BIG-STRAIGHT.
* Checks for dice 2, 3, 4, 5, 6.
IF WS-DICE-COUNTS(2) = 1 AND
WS-DICE-COUNTS(3) = 1 AND
WS-DICE-COUNTS(4) = 1 AND
WS-DICE-COUNTS(5) = 1 AND
WS-DICE-COUNTS(6) = 1
MOVE 30 TO LS-SCORE
END-IF.
Detailed Code Walkthrough
Let's break down the logic of our Cobol program, section by section.
1. Data Division: Setting Up Our Workspace
In the WORKING-STORAGE SECTION, we define variables internal to our program. The most important is WS-DICE-COUNTS, a table defined with OCCURS 6 TIMES. This acts as an array where the index represents the die face (1-6) and the value at that index stores its frequency. For example, if WS-DICE-COUNTS(4) is 3, it means the number '4' appeared three times in the dice roll.
In the LINKAGE SECTION, we define the data items that act as the program's interface. LS-DICE-INPUT is a table to receive the five dice, LS-CATEGORY receives the category name, and LS-SCORE is where we'll place our final calculated score before returning control to the calling program.
2. Procedure Division: The Main Logic Flow
The execution starts at the first paragraph, MAIN-LOGIC.
INITIALIZE: We first zero-out all our working storage variables to ensure a clean slate for every run.PERFORM COUNT-DICE-FREQUENCIES: This is the crucial first step. We call a helper paragraph to loop through the input dice and populate ourWS-DICE-COUNTStable. This pre-processing makes all subsequent calculations much simpler.EVALUATEstatement: This is Cobol's powerful equivalent of aswitchormatchstatement. It cleanly directs the program flow based on the value ofLS-CATEGORY. EachWHENclause performs the specific paragraph responsible for calculating that category's score. UsingFUNCTION UPPER-CASEmakes the check case-insensitive.GOBACK: This statement returns control to the program that called our subprogram, with the final score now stored inLS-SCORE.
3. The Helper Paragraphs: Where the Real Work Happens
Each paragraph is a self-contained unit of logic for a specific task.
COUNT-DICE-FREQUENCIES: This is our data analysis engine. A simplePERFORM VARYINGloop iterates through the five input dice. For each die, it uses its value as an index to increment the corresponding counter inWS-DICE-COUNTS.
● Start Frequency Count
│
▼
┌──────────────────┐
│ Initialize │
│ WS-DICE-COUNTS │
│ (all to zero) │
└─────────┬────────┘
│
▼
◆ Loop I from 1 to 5?
╱ ╲
Yes No
│ │
▼ │
┌────────────────┐ (Loop End)
│ Get LS-DICE(I) │
└───────┬────────┘
│
▼
┌───────────────────────────┐
│ Let V = value of LS-DICE(I) │
│ Increment WS-DICE-COUNTS(V) │
└───────────────────────────┘
│
└───────────────→ (Back to Loop)
│
▼
● End Count
CALCULATE-NUMBERED-CATEGORY: A clever, reusable paragraph. Instead of having separate logic for "Ones", "Twos", etc., we use one paragraph. TheEVALUATEstatement calls it with a loop counterIalready set to the target number (e.g., for "Twos", it's called withI=2). The score is then simply the count of that die (WS-DICE-COUNTS(I)) multiplied by its face value (I).CALCULATE-FULL-HOUSE: This paragraph checks our frequency table. It iterates from 1 to 6, looking for a number that appeared twice (a pair) and a number that appeared three times (a triple). If both conditions are met, it callsCALCULATE-CHOICEto sum up all the dice for the final score.CALCULATE-LITTLE-STRAIGHT&CALCULATE-BIG-STRAIGHT: These are the most direct checks. They use a compoundIFstatement to verify that the counts for the required dice (1-5 for Little, 2-6 for Big) are all exactly1. If so, the score is a fixed 30 points.
Compiling and Running Your Cobol Program
Writing the code is only half the battle. To see it in action, you need to compile and run it. We'll use GnuCOBOL (formerly OpenCOBOL), a popular, free, and open-source Cobol compiler that works on Linux, macOS, and Windows.
Step 1: Installation
If you don't have GnuCOBOL installed, you can typically get it through your system's package manager.
On Debian/Ubuntu:
sudo apt-get update
sudo apt-get install gnucobol
On macOS (using Homebrew):
brew install gnu-cobol
Step 2: Creating a Driver Program
Since our scorer is a subprogram, we need a main program to "drive" it—to call it with some test data. Let's create a file named yacht-driver.cob:
IDENTIFICATION DIVISION.
PROGRAM-ID. YACHT-DRIVER.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-DICE-VALUES.
05 DICE-VALS PIC 9(1) OCCURS 5 TIMES.
01 WS-TEST-CATEGORY PIC X(20).
01 WS-FINAL-SCORE PIC 9(2).
PROCEDURE DIVISION.
MOVE 3 TO DICE-VALS(1).
MOVE 3 TO DICE-VALS(2).
MOVE 3 TO DICE-VALS(3).
MOVE 5 TO DICE-VALS(4).
MOVE 5 TO DICE-VALS(5).
MOVE "FULL HOUSE" TO WS-TEST-CATEGORY.
DISPLAY "Testing Category: " WS-TEST-CATEGORY.
DISPLAY "With Dice: " DICE-VALS(1) " " DICE-VALS(2) " "
DICE-VALS(3) " " DICE-VALS(4) " "
DICE-VALS(5).
CALL "YACHT-SCORER" USING WS-DICE-VALUES,
WS-TEST-CATEGORY,
WS-FINAL-SCORE.
DISPLAY "Calculated Score: " WS-FINAL-SCORE.
STOP RUN.
Step 3: Compilation and Execution
Save the scorer code as yacht-scorer.cob and the driver as yacht-driver.cob. Now, open your terminal and run the following commands:
# Compile the subprogram into a dynamically loadable module
cobc -m -o yacht-scorer.so yacht-scorer.cob
# Compile the main driver program and link it
cobc -x -o yacht-driver yacht-driver.cob
# Run the executable
./yacht-driver
You should see the following output, confirming that your logic for "Full House" is working correctly:
Testing Category: FULL HOUSE
With Dice: 3 3 3 5 5
Calculated Score: 19
Pros & Cons of the Cobol Approach
Every technological choice involves trade-offs. While Cobol provides a robust solution, it's valuable to compare it with how one might solve the same problem in a more modern, general-purpose language like Python.
| Aspect | Cobol Approach | Modern Language (e.g., Python) Approach |
|---|---|---|
| Readability | Extremely high due to verbose, English-like syntax. Logic is self-documenting but can be lengthy. | High, but relies on concise syntax and idioms. Can be less clear to non-Python developers. |
| Data Structure | Rigid and explicit. Data types and sizes are defined upfront, which prevents many types of runtime errors. | Flexible and dynamic. Uses dictionaries (hash maps) for frequency counts, which is very efficient but less type-safe. |
| Boilerplate Code | High. The division structure, data definitions, and verbose syntax require more lines of code for simple tasks. | Low. Built-in functions and libraries (like `collections.Counter`) can solve parts of the problem in a single line. |
| Development Cycle | Slower. Requires a separate compilation step. Debugging can be more involved depending on the environment. | Faster. Interpreted nature allows for rapid testing and iteration without a compile step. |
| Performance | Excellent. Compiled to native machine code, making it very fast for raw computation and data manipulation. | Generally slower for CPU-bound tasks due to its interpreted nature, but often "fast enough" for most applications. |
| Ecosystem | Limited open-source libraries. Primarily focused on enterprise and mainframe environments. | Vast ecosystem of libraries for any imaginable task, from data science to web development. |
Frequently Asked Questions (FAQ)
- 1. Is Cobol still relevant for new projects?
-
While you wouldn't typically choose Cobol for a new web application or mobile app, it is still extremely relevant and actively developed for the environments it excels in: large-scale, high-volume transaction processing systems in finance, insurance, and government. Billions of lines of critical Cobol code are still in production, and modernizing and maintaining these systems is a major field of software engineering. Learning it provides a unique and valuable skill set. For more on the language, check out our complete Cobol language guide.
- 2. Why use a
PERFORM VARYINGloop? -
PERFORM VARYINGis Cobol's standard, most powerful tool for creating "for" loops. It provides a clean and structured way to iterate a fixed number of times (like through our 5 dice) or until a certain condition is met. It explicitly handles the initialization, condition check, and increment of a loop counter (likeIin our code), making the logic clear and less prone to off-by-one errors. - 3. What is the difference between
PIC 9andPIC S9? -
The
PIC(Picture) clause defines a variable's type and size.PIC 9defines a numeric field that can only hold unsigned integers (0-9). For example,PIC 9(2)can hold values from 0 to 99.PIC S9defines a signed numeric field, meaning it can hold positive and negative values. The 'S' is stored efficiently in the data representation and is crucial for financial calculations where debits and credits are common. - 4. How could I handle invalid input for the category?
-
Our
EVALUATEstatement already has a basic mechanism for this. The optionalWHEN OTHERclause acts as a default case. If the input category doesn't match any of the specifiedWHENconditions, the logic withinWHEN OTHERis executed. In our program, we implicitly let the score remain 0, which is a safe default. You could also set an error flag or return a specific status code. - 5. Why was this program structured as a callable subprogram?
-
Structuring code into reusable, callable modules is a cornerstone of good software engineering, and Cobol is no exception. By making the scorer a subprogram, it becomes a "black box." A main application (like a full game engine) can simply
CALLit with the required inputs and trust it to return the correct score, without needing to know about its internal workings. This promotes modularity, testability, and code reuse. - 6. What does
OCCURS 5 TIMESmean in theDATA DIVISION? -
The
OCCURSclause is Cobol's way of defining a table, which is equivalent to an array in other languages. The statement05 LS-DICE PIC 9(1) OCCURS 5 TIMEScreates a data structure namedLS-DICEthat is a list of 5 elements, where each element is a single-digit number (PIC 9(1)). You can then access individual elements using an index, likeLS-DICE(1),LS-DICE(2), and so on. - 7. Are there modern IDEs for Cobol development?
-
Absolutely. The days of only using green-screen editors are long gone. Modern developers often use IDEs like VS Code with extensions such as "COBOL Language Support" or "IBM Z Open Editor", which provide syntax highlighting, code completion, and debugging capabilities. For enterprise mainframe development, products like IBM's Developer for z/OS (IDz) offer a full-featured, Eclipse-based environment.
Conclusion: Logic, Structure, and Timeless Power
We've journeyed through the intricate rules of the Yacht dice game and emerged with a clean, robust, and efficient solution written in Cobol. This exercise from the kodikra learning path does more than just solve a puzzle; it fundamentally demonstrates that the principles of good software design—clear logic, structured data, and modularity—are timeless. Cobol, with its deliberate syntax and disciplined structure, forces us to be methodical programmers, a skill that is invaluable regardless of the language you use.
You've seen how to manipulate tables, implement complex conditional logic with EVALUATE, and structure a program for reusability. You've proven that Cobol is more than capable of handling complex, rule-based problems with an elegance and clarity that might surprise many. The next time you encounter a complex set of business rules, you'll have a new appreciation for the tools and techniques that have reliably powered our digital world for over sixty years.
Technology Disclaimer: The code and commands in this article were verified using GnuCOBOL 3.1.2. While Cobol has strong backward compatibility, syntax and compiler flags may vary slightly with other compilers or versions. Always consult your environment's documentation.
Published by Kodikra — Your trusted Cobol learning resource.
Post a Comment