Git Product home page Git Product logo

calculator-game's Introduction

Calculator Game

A clone and solver of Calculator: The Game by Simple Machine. It supports all 17 rule types. It supports portals. Users can play and solve all 199 levels of the game, plus any custom level they want to create. It runs through the terminal.

The solver prompts for input about a particular level, and then beats the level. It outputs all solutions to the user. It then prompts the user to complete another level or quit the program. This repeats until the user chooses to quit.

The clone prompts for a first level (1 to 199), and then displays that level. The user is able to enter a rule, quit the game, or restart the level at any time. If the user beats a level, the clone prompts the user to continue to the next level or quit. This repeats until the user quits or beats level 199.

This document covers the rules of Calculator: The Game and how to use the program. For installation instructions, see the usage docs. If you have any issues with the program, feel free to open an issue on the repository.

The Main Program

Sample level

In Calculator: The Game the user is presented with a simple challenge: change the calculator's value to the goal value by pressing the buttons (AKA applying the rules). The player is limited to a number of total moves before they lose the game. The value is displayed in large digits just above the buttons. Each button corresponds to a rule (detailed below) and changes the value in different ways.

A pair of portals may be part of the level. The left portal will be below one digit in the value, and the right portal will be above a different digit. The portals don't move. The digits will "fall into" the left portal and "fall out of" the right portal, adding to the level's value. This behavior is detailed on the portals docs.

To solve a level, pass in the details through the terminal, and the solution will be printed to standard output.

Rules

The game has 16 rules, detailed below. The code parentheses wrap placeholder values: replace the placeholder values with digits and leave off the parentheses when giving input to the program.

  • Add (+(op1)), subtract (-(op1)), multiply (*(op1)), and divide (/(op1)) function as expected
  • Pad ((op1)) pads op1 to the right of value. op1 must be positive
    • 1 pad 2 becomes 12
    • 12 pad 34 becomes 1234
  • Sign (+/-) changes the sign of value
  • Delete (<<) deletes the rightmost digit of value
    • 1234 delete becomes 123
  • Convert ((op1)=>(op2), "convert op1 to op2") converts all instances of op1 to op2
    • 1234 convert 34 to 89 becomes 1289
  • Power (^(op1)) raises value to the power of op1
  • Reverse (Reverse) reverses the order of the digits in value
  • Sum (SUM) changes value into the sum of its digits but preserves the sign of value
    • -123 SUM becomes -6 (because 1 + 2 + 3 = 6)
  • Shift left (< Shift) and shift right (Shift >) shift the digits of value one position left or right, respectively
    • 1234 shift left becomes 2341
    • 1234 shift right becomes 4123
  • Mirror (Mirror) appends the mirrored value to the end of value
    • 23 mirror becomes 2332
  • Meta Add ([+](op1)) adds op1 to all operands of non-meta rules in the level
  • Store (Store) is the only rule that can be updated
    • To update the Store rule, enter Update Store
    • The Store rule cannot be applied until it is updated
    • Upon updating the Store rule, its operand becomes the value of the level
    • Applying the Store rule functions just like applying the Pad rule
    • The Store rule can be updated any number of times, but updating it does decrease the move counter
    • There can only be one Store rule in a level
  • Inverse Ten (Inv10) converts each digit to its "10-additive inverse"
    • 4 inverse ten becomes 6, because 4 + 6 = 10
    • 123 inverse ten becomes 987, because each digit is evaluated independently

For more detailed rule documentation, see the rule docs.

Use Case

Sample Play Use Case

Welcome to Calculator: The Game!
Enter the first level: (1 to 199, inclusive): 1
Level 1
goal: 2
moves: 2
portals: null
rules: ['+1']
value: 0

Enter rule to apply ('quit' to quit, 'clear' to restart the level): +1
goal: 2
moves: 1
portals: null
rules: ['+1']
value: 1

Enter rule to apply ('quit' to quit, 'clear' to restart the level): +1
Congratulations, you beat the level!
Next level? (y/n): n
You got to level 2.
Goodbye!

Sample Solver Use Case

Enter start value: 2
Enter goal value: 5
Enter the number of moves: 3
Enter one rule per line (empty string to mark end of list):
+2
+1

Are there any portals for this game? (y/n): n
Solution:
+1
+2
Solve again (y/n): y
Enter start value: 3002
Enter goal value: 3507
Enter the number of moves: 5
Enter one rule per line (empty string to mark end of list):
7
Shift >

Are there any portals for this game? (y/n): y
Enter the distance from the ones place of the portal on the left: 5
Enter the distance from the ones place of the portal on the right: 0
Solution:
7
Shift >
7
7
7
Solve again (y/n): n

To go from 2 to 5 in at most 3 moves using the rules "add 2" and "add 1", a solution is to first add 1, then add 2. Although 3 moves are allowed, only 2 moves are needed. Other solutions exist, but this one is the first one found by the program, so it is the one output.

Let's examine the second part of the above use case (going from 3002 to 3507):

Start:
     Y
  3002
Y

'7':
     Y
 30027
Y

'Shift >':
     Y
 73002
Y

'7':
     Y
730027
Y

     Y
 30034
Y

'7':
     Y
300347
Y

     Y
   350
Y

'7':
     Y
  3507
Y

Developer Mode

Running the developer mode allows for test cases to be written automatically via a test factory while using the solver as normal. For more information, see Testing: Test Cases

Roadmap

This program is enough to beat the original game and any custom level. I want to do more.

Eventually, I'd like to:

  • Add a feature to only display hints on demand
  • Write my own levels and custom rules
  • Share levels and rules with friends
  • Show detailed instructions
  • Present through a good UI (and restrict bad input)

View the issues and PRs to see what's in development.

Further Documentation

All further documentation is found in the docs folder indexed here.

calculator-game's People

Contributors

mark-wiemer avatar

Watchers

 avatar  avatar

calculator-game's Issues

Abstract pad rule logic

Description
The pad rule logic is duplicated in PadRule.java and StoreRule.java.

Problems
This isn't DRY

Proposed Solution
Have StoreRule call a package visible method in PadRule

Acceptance Criteria

  • DRY code

Update Java formatter

Description
Some of this code is super gross due to weird formatting behaviors

Acceptance Criteria

  • The formatter doesn't make the code so gross

Hide Rule subclass constructors

Description
Rule subclass constructors are usually public

Problems
Other packages shouldn't be able to create rules except through the Rule.makeRule method.

Proposed Solution
Make Rule subclass constructors package-level visible.

Acceptance Criteria

  • External clients cannot instantiate any Rule subclass directly through its constructor

Write changelog based on tags and commit history

Description
Documenting the history of this project would be good practice to better understand how it has historically functioned

Acceptance Criteria

  • At least a short bulleted list for each release (1+ bullet points), all within one file

Developer quit shouldn't be 'n '

Description
Currently, to quit Developer.main, the user enters Config.QUIT == "n "

Problems
That's a gross entry. It ends with a space.

Proposed Solution
Trim the comparison so that n is also valid input

Acceptance Criteria

  • Better Developer quit entry logic

Make sure Meta Store rules cannot be created by external clients

Description
The Meta Store rule should be only internally represented and instantiable. Although not mission critical, it would be nice to ensure that this is the case. If external users can create Meta Store rules, they may run into bugs and other undefined behaviors.

Acceptance Criteria

  • Ensure MetaStoreRule cannot be instantiated by external clients

Split Config into multiple files

Description
Right now, Config.java acts as one monolith of nearly all config settings

Problems
Monoliths are bad and violate SRP. It's becoming difficult to read/maintain and it's unclear exactly what code belongs there

Proposed Solution
Split config into several files

Acceptance Criteria

  • The purpose of Config is clear
  • New files are created to host Config logic, and their purposes are also clear

I/O logic isolation

Description
Currently, I/O logic (printing prompts, getting input, enforcing typed input) is scattered about the three classes in the main package

Problems
Logic is probably not DRY. It's also not the most readable

Proposed Solution
Create a new main.IO class to handle all IO

Acceptance Criteria

  • No more print statements within any main class (This was a bad criterion as simple print statements are allowed)
  • No more scanners within any main class (Using scanners is good design)
  • All complex I/O logic is moved to a single module

Streams, streams, everywhere, streams!

Description
Lots of functions convert lists from one type to another manually without a consistent interface.

Problems
Cluttered, clunky code is hard to maintain

Proposed Solution
Java 8 Streams! Read all about them here and use maps and stuff!
All arrays (Rule[] rules, int[] portals, etc.) should be converted to lists (preferably immutable, but that's for another issue) for easier stream usage.

Acceptance Criteria

  • No more clunky list conversion code, if possible
  • Code should remain readable: we aren't using streams for the sake of streams. If it's

Meta Store / Update Store clarifications

Description
The Meta Store rule is currently hidden from users.

Problems
Program functionality is ambiguous to users. They also cannot use Meta Store rules in the future "play" logic

Proposed Solution
The Meta Store rule should not be necessarily tied to a Store rule. If it is added to a game without a Store rule, an exception should be thrown. If it is explicitly added to a game by a user, a message should be printed to tell users that it is not necessary to add the rule.

Acceptance Criteria

  • Meta Store rule should be renamed Update Store rule
  • Meta Store rule independent of Store rule
  • Exception Generate null when adding a Meta Store rule to a game without a Store rule
  • Users should be able to create Meta Store rules

Document control flow

A control flow diagram for the main loops would be a nice overview of how the project works

Providing command-line arguments shouldn't prompt for more input

Is your feature request related to a problem? Please describe.
When I provide command-line arguments, I only want to solve/play one level. Do not prompt me to enter an infinite loop, as that makes it more difficult to automate usage of the project.

Describe the solution you'd like
Don't prompt for infinite loop, just return 0 if game was solved, nonzero otherwise with informative error message

Describe alternatives you've considered
Currently I have to wrap all scripts using this with "no" inputs and provide that as well, wrapped in a scanner. Lots of maintenance code when a simple fix is possible

Additional context
N/A

Acceptance criteria

  • Don't prompt me to enter an infinite loop if I provide command-line arguments
  • Give me a return code of 0 if nothing went wrong, nonzero otherwise

Refactor tests

Tests are currently not very granular and, in general, poorly organized.

Acceptance Criteria:

  • MainTests.assertCreatesGame becomes two methods: one for the scanner, one for CLA
  • Stay DRY with fixture-like functionality
  • Remove printing from passing tests (#41)
  • IoUtils interface becomes instance-based, not static, is improved (see #17)
  • Remove redundant tests (LevelTests is redundant, already tested within PlayTests)
  • Tests are at top of class

Replace Rule.makeRule and Rule.ruleFromString with constructor

Description
Currently Rule instances are created using Rule.makeRule and Rule.ruleFromString

Problems
This is not the default way to instantiate a type, and surprises the developer, making the code harder to follow.

Proposed Solution
Use constructors

Acceptance Criteria

  • No more Rule.makeRule
  • No more Rule.ruleFromString

Investigate using Optionals

Description
Creating instances of CalculatorGame currently results in null often or throwing exceptions. Maybe Optional would fix this issue?

Acceptance Criteria

  • Understand the basics of Optional in Java
  • Understand the use cases of Optional
  • Decide whether using Optional is beneficial in this project

IoUtils improvements

Description
Static methods are used to build and tear down the testing environment

Problems
Static methods only have one return value and are stateless. This means the client must track things instead, violating SRP and cluttering client code

Proposed Solution
Make IoUtils methods instance-based. This way, the IoUtils instance can store the default System.out and can build up / tear down testing environments with one method call

Acceptance Criteria

  • IoUtils methods are instance-based
  • One-line usage within all clients

Catch bad input

For the Main class, any old input is allowed and it's super gross. The user should never see exceptions.

Naming schemes around "test case", "game", and "level"

Description
The naming is murky: transitioning from "game" to "level" hasn't been consistent, and what exactly a "test case" is compared to a "level" isn't immediately clear.

Problems
Ambiguity is bad!

Proposed Solution

  • References (in method names, variable names, comments, and documentation) to an instance of what is currently CalculatorGame should call it a level.
  • The solver.Game interface should probably be renamed solver.Level.
  • The CalculatorGame class should be renamed, but remain concise, maybe CalculatorLevel?

Acceptance Criteria

  • A clear naming scheme is chosen and implemented across the entire repository

Allow users to create their own rules

Is your feature request related to a problem? Please describe.
As a user,
I want to be able to create my own rules.
That would be really fun for me.

Describe the solution you'd like
Let me input a rule somehow to the program and then create levels using that rule. Ideally, the rule is persistent across runs.

Describe alternatives you've considered
Right now, as a user, I can't create my own rules at all.

Additional context
N/A

Acceptance criteria

  • I can create my own rules within the Solver control flow (currently main.Main)
  • Bonus: The rules stay persistent across runs

Interface segregation of Rule

Description
Currently, all Rule instances have a getOperand1 and getOperand2 method. For many instances, these methods are unnecessary.

Problems
Violates the interface segregation principle, leading to potential issues when clients use those methods on Rule instances that do not define them

Proposed Solution
Subclass Rule with 'zero operators' type, 'one operator' type, and 'two operator' type.

Acceptance Criteria

  • Implement ISP in Rule and its subclasses

Game construction logic

Description
Currently, clients build a state (value, movesLeft, rules) and pass that to CalculatorGame.generateGame, which may return null, but otherwise returns a CalculatorGame instance

Problems
Using an intermediate method instead of a constructor is surprising, making the code more difficult to follow for newcomers to the project. Returning null can, as always, result in surprises

Proposed Solution
N/A

Acceptance Criteria

  • Game construction logic is clear and unsurprising

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.