manual - GlenDC/cgreader GitHub Wiki

A Go package to run and validate the Codingame programs offline on your computer.

Index

  1. Types of programs
  2. Manual Program
  3. Run a manual program
  4. Run and validate a manual program
  5. Run your program with multiple input files
  6. Target Program
  7. Predefined Target Challenges 1. Run with multiple input files 1. How to convert your offline PT solution code to use online? 1. Ragnarok Example
  8. Template and Example
  9. Run a program in Sandbox Mode
  10. Challenge map in your terminal
  11. Reset Callback

This document describes the logic behind cgreader. It is only meant for developers who want to contribute to the cgreader library, which has been written in Go. In case you are an end user you should read the readme of this repository file or the home page of this wiki.

Types of programs

Codingame has a lot challenges. These challenges can be devided in types of programs based on how they receive input and what the goal of the challenge is.

  1. Manual Program: This is the most simple program and just requires you to write a simple main function that takes a string channel as its input. This channel will give you the input line by line and it's up to you how to interpret the received input. The output of the program has to be returned via the output channel.
  2. Target Program: Some challenges are based on win and lose conditions. These are the most complex program and require extra work from the user in order to do these challenges offline, as you'll have to write the logic of the challenge, on top of your usual challenge code. Because of this there are the predefined challenge programs, that do all this hard work for you. But anyway... How does a target program work?
  3. You'll write a struct based on the TargetProgram interface
  4. The initial input will be parsed and have to be manually interpred by you via the InitialInput method.
  5. The program runs and calls each frame the Update method, using the input given via the GetInput method. Update will return your output for that frame via the output channel. 1. This output can also be traced if wanted.
  6. Each frame your output will be used and update the game state via the SetOutput method
  7. The program exits if the LoseConditionCheck- or/and WinConditionCheck method returns true

Manual programs can:

  1. run the program or
  2. run and validate the program based on a test text file

With both options you can also echo your output if wanted.

Suggestions to improve a type of program, or to define a new type of program are welcome and can be filed as an issue or a pull request.

Manual Program

Run a manual program

Template

package main

import (
  "github.com/glendc/cgreader"                      // cgreader package
)

func main() {
  cgreader.RunManualProgram(
      "<INPUT TEXT FILE>",                          // program input source
      func(input <-chan string, output chan string) {            
          // your solution here
  })
}

Example

package main

import (
  "fmt"
  "github.com/glendc/cgreader"
  "strings"
)

func main() {
  cgreader.RunManualProgram(
    "../../input/ascii_1.txt",
    func(input <-chan string, output chan string) {
      var width, height int
      var text string

      fmt.Sscanln(<-input, &width)
      fmt.Sscanln(<-input, &height)
      fmt.Sscanln(<-input, &text)

      text = strings.ToUpper(text)

      ascii := make([]string, height)
      for i := 0; i < height; i++ {
        ascii[i] = <-input
      }

      lines := make([]string, height)
      for _, char := range text {
        character := int(char) - 65
        if character < 0 || character > 26 {
          character = 26
        }
        for i := range lines {
          position := character * width
          lines[i] += ascii[i][position : position+width]
        }
      }

      for _, line := range lines {
        output <- line
      }
    })
}
Output:
### 
#   
##  
#   
### 

Run and validate a manual program

Template

package main

import (
    "github.com/glendc/cgreader"                      // cgreader package
)

func main() {
    cgreader.RunAndValidateProgramManual(
        "<INPUT TEXT FILE>",                          // program input file
        "<OUTPUT TEXT FILE>",                         // expected output file
        true,                                         // show output?
        func(input <-chan string, output chan string) {               // program main
            // your solution here
        })
}

Example

package main

import (
  "fmt"
  "github.com/glendc/cgreader"
  "strings"
)

func main() {
  cgreader.RunAndValidateManualProgram(
    "../input/ascii_1.txt",
    "../output/ascii_1.txt",
    true,
    func(input <-chan string, output chan string) {
      var width, height int
      var text string

      fmt.Sscanln(<-input, &width)
      fmt.Sscanln(<-input, &height)
      fmt.Sscanln(<-input, &text)

      text = strings.ToUpper(text)

      ascii := make([]string, height)
      for i := 0; i < height; i++ {
        ascii[i] = <-input
      }

      lines := make([]string, height)
      for _, char := range text {
        character := int(char) - 65
        if character < 0 || character > 26 {
          character = 26
        }
        for i := range lines {
          position := character * width
          lines[i] += ascii[i][position : position+width]
        }
      }

      for _, line := range lines {
        output <- line;
      }
    })
}
Output:
### 
#   
##  
#   
### 

Program is correct!

Run your program with multiple input files

Running each input file one by one is quite tedious. Therefore it is possible to run multiple input files at once, both for validated and normal manual programs. The definition of these functions is similar, except for the file parameter(s).

Examples
Multiple manual programs
// run multiple manual programs at once
// the output is seperated by a newline character
func RunManualPrograms(input []string, main ProgramMain)
Multiple validated manual programs
// run and validated multiple manual programs at once
// the output is seperated by a newline character
func RunAndValidateManualPrograms(input, test []string, echo bool, main ProgramMain)

You can take a look at the Scrabble Challenge Solution, as a practical example.

tip: you can use the func GetFileList(format string, n int) (files []string) help function for a less error-prone and time saving approach.

Target Program

Predefined Target Challenges

The target program challenge type was created to allow you to play more complex challenges, such as Ragnarok, offline. However with target programs you still need to program the Challenge and AI logic yourself, which isn't the goal of the Codingame challenges at all. The Predefined Target Challenges allow you to start on the challenge instantly and it keeps your code base exactly the same as if it were an online submission.

Run with multiple input files

Running each input file one by one is quite tedious. Therefore it is possible to run multiple input files at once. The definition of this function is similar to the normal one, except for the input file parameter.

Examples
// run len(input) amount of times the target program
// with each time a different input file.
func RunTargetPrograms(input []string, trace bool, program TargetProgram)

// each predefined target challenge allows you to do the same in a similar fashion
// example for the ragnarok challenge:
func RunRagnarokPrograms(input []string, trace bool, initialize UserInitializeFunction, update UserUpdateFunction)

You can take a look at the Ragnarok Giants Challenge Solution, as a practical example.

tip: you can use the func GetFileList(format string, n int) (files []string) help function for a less error-prone and time saving approach.

How to convert your offline PT solution code to use online?

There is no real reason why you would want to convert your offline PT challenge code, but let's say you want to do so. It's possible and easy, as your code base will remain quite similar.

Let's say we have the follow psuedo offline Ragnarok PT solution code:

package man

import (
  "github.com/glendc/cgreader"
  // packages...
)

// definition of functions, types and variables...

func Initialize(input <-chan string) {
  // parse the initial input, no output expected...
}

func Update(input <-chan string, output chan string) {
  // the code of your solution logic will be defined here...
}

func main() {
  cgreader.RunRagnarokProgram("ragnarok_1.txt", true, Initialize, Update)
}

After your converted this code manually, you will end up with the following online version:

package main

import (
   // packages...
)

// definition of functions, types and variables...

func main() {
  // parse the initial input, no output expected...
  
  for {
    // the code of your solution logic will be defined here...
    fmt.Print("output")
  }
}

As you can see it's quite similar. On top of this you'll have to convert code that makes use of the channel input parameter, to use the standard input instead. (e.g. fmt.Sscanf(<-input to fmt.Scanf( and output <- fmt.Sprintf to fmt.Printf)

Ragnarok Example

You can find my ragnarok solution code here. Thanks to all the predefined Ragnarok logic, I can simply run the program via the cgreader.RunRagnarok function, which makes use of the default way to run a Target Program defined here. All this results in a clean and good looking solution.

Template and Example

Template

package main

import (
  "github.com/glendc/cgreader"
)

type Program struct {
  // define a structure, and optionally define member variables
}

func (program *Program) ParseInitialData(ch <-chan string) {
  // parse the initial data, just like in a manual program
}

func (program *Program) GetInput() (input chan string) {
  input = make(chan string)
  go func() {
    // pass the challenge input into the channel
  }()
  return
}

func (program *Program) Update(input <-chan string, output chan string) {
  // your solution logic will be defined here
}

func (program *Program) SetOutput(output string) string {
  // define challenge logic using the output of the user
  // return optional trace message
}

func (program *Program) LoseConditionCheck() bool {
  // return true if user has lost
}

func (program *Program) WinConditionCheck() bool {
  // return true if user has won
}

func main() {
  cgreader.RunTargetProgram( // Execute your program offline
    "program_1.txt", // The challenge input file
    true,            // Trace output && state?
    &Program{})      // Create the program && pass it by reference
}

Example

Take a look at the ragnarok predefined target challenge, which is a simple and clear example on how to implement a target program. It also shows that it requires much more coding and knowledge to develop one, than to actually solve it, which is the reason why a raw target program shouldn't be used by end-users.

Run a program in Sandbox Mode

Besides running pure Codingame challenges, you might want to run a program without any input and/or validation needed. This is especially true when developing in a language parsed via this repository, such as brainfuck. This is made possible via the RunSandboxProgram

 type SandboxProgramFunction func(chan string)

 // function that runs a function, handles it output, including timeout functionality
 func RunSandboxProgram(main SandboxProgramFunction)

Challenge map in your terminal

For challenges like ragnarok you might want to have a map, like you would have in the online Codingame version. For this you can use the cgreader.DrawMap(...) function. You can see a working ragnarok solution with map here.

Ragnarok Map Example

Map after first move:
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  T  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  H  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  +  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
Map after last move:
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  H  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  +  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  +  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  +  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  +  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  +  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  +  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  +  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  +  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  +  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  +  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  +  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  +  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  +  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  +  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  +  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  +  .  .  .  .  .  .  .  .  
.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  . 

Reset Callback

In case you're supporting another language that requires that you initialize some variables (like a memory buffer) just before the program excecutes you can put this logic within a function and give it to the cgreader system as a callback via the SetResetProgramCallback function:

type ProgramResetCallback func()

// call this function (codingame.SetResetProgramCallback)
SetResetProgramCallback(callback ProgramResetCallback)