4.1 – CodinGame

Avant-propos

Codingame est une plateforme en ligne de programmation proposant des exerces de logique à résoudre dans le langage de son choix.

Un problème se présente sous la forme d’un document expliquant la fonction a réaliser, la définition des entrées et sorties, des exemples de vecteurs de test, des scénarios pour tester le programme pendant le développement et des vecteurs de test « cachés » pour valider l’algorithme.

L’interface de développement supporte 27 langages de programmation. Je l’ai utilisée pour me confronter à plusieurs approches de la sémantique du code et de l’algorithmie ainsi que pour approfondir mes connaissances en C et me former aux langages de programmation orienté objets, notemment Java et C#.

Vous trouverez ci-dessous deux exemples de “problèmes” posés par le site, ainsi que ma solution. Les problèmes sont posés en anglais.

Exemple 1- problème simple: “Logic Gates”

The Goal:

A logic gate is an electronic device implementing a boolean function, performing a logical operation on one or more binary inputs and producing a single binary output.

Given n input signal names and their respective data, and m output signal names with their respective type of gate and two input signal names, provide m output signal names and their respective data, in the same order as provided in input description.

All type of gates will always have two inputs and one output.
All input signal data always have the same length.

The type of gates are :
AND : performs a logical AND operation.
OR : performs a logical OR operation.
XOR : performs a logical exclusive OR operation.
NAND : performs a logical inverted AND operation.
NOR : performs a logical inverted OR operation.
NXOR : performs a logical inverted exclusive OR operation.

Signals are represented with underscore and minus characters, an undescore matching a low level (0, or false) and a minus matching a high level (1, or true).


Game Input:

Line 1 : n number of input signals.
Line 2 : m number of output signals.
n next lines : two space separated strings : name of input signal, then signal form.
m next lines : four space separated strings : name of output signal, then type of logic gate, then first input name, then second input name.

Output:
m lines : two space separated strings : name of output signal, then signal form.

Constraints:
1 ≤ n ≤ 4
1 ≤ m ≤ 16


Example:

Input

2
3
A __---___---___---___---___
B ____---___---___---___---_
C AND A B
D OR A B
E XOR A B

Output

C ____-_____-_____-_____-___
D __-----_-----_-----_-----_
E __--_--_--_--_--_--_--_--_


Link to the puzzle: https://www.codingame.com/ide/puzzle/logic-gates

import java.util.*;
import java.io.*;
import java.math.*;

/**
 * Auto-generated code below aims at helping you parse
 * the standard input according to the problem statement.
 **/
class Solution {

    public static void main(String args[]) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        Map<String, String> inputSignalsMap = new HashMap<String, String>();
        String[] outputStrings = new String[m];

        for (int i = 0; i < n; i++) {
            String inputName = in.next();
            String inputSignal = in.next();
            inputSignalsMap.put(inputName, inputSignal);
        }
        for (int i = 0; i < m; i++) {
            String outputName = in.next();
            outputStrings[i] = outputName + " ";
            String type = in.next();
            String inputName1 = in.next();
            String inputName2 = in.next();
            char[] signal1 = inputSignalsMap.get(inputName1).toCharArray();
            char[] signal2 = inputSignalsMap.get(inputName2).toCharArray();
            
            for (int j=0; j<signal1.length; j++)
            {
                boolean sb1 = charToBool(signal1[j]);
                boolean sb2 = charToBool(signal2[j]);
                outputStrings[i] += boolToChar(booleanAlgebra(type, sb1, sb2));
            } 

            System.out.println(outputStrings[i]);



        }
        for (int i = 0; i < m; i++) {

            // Write an answer using System.out.println()
            // To debug: System.err.println("Debug messages...")
            //System.out.println("output name and signal");
        }
    }

    public static boolean charToBool(char c) {
    // c = Character.toLowerCase(c);
    switch (c) {
        case '-':
            return true;
        case '_':
            return false;
        default:
            throw new IllegalArgumentException("Must be either '_' or '-'.");
        }   
    }  

    public static char boolToChar(boolean b) {
    if (b) {return '-';}
    else {return '_';}
    }     

    public static boolean booleanAlgebra(String function, boolean bool1, boolean bool2 ) {
    switch (function) {
        case "AND":
            return (bool1 & bool2);
        case "OR":
            return (bool1 | bool2);
        case "XOR":
            return (bool1 ^ bool2);
        case "NAND":
            return !(bool1 & bool2);
        case "NOR":
            return !(bool1 | bool2);
        case "NXOR":
            return !(bool1 ^ bool2);
        default:
            throw new IllegalArgumentException("Invalid function");
        }   
    } 
}


Exemple 2 – problème plus complexe: “The Labyrinth

The Goal:

Once teleported inside the structure, your mission is to:

  • find the control room from which you will be able to deactivate the tracker beam
  • get back to your starting position once you’ve deactivated the tracker beam

Rules:
The structure is arranged as a rectangular maze composed of cells. Within the maze Rick can go in any of the following directions: UPDOWNLEFT or RIGHT.

Rick is using his tricorder to scan the area around him but due to a disruptor field, he is only able to scan the cells located in a 5-cell wide square centered on him.

Unfortunately, Spock was correct, there is a trap! Once you reach the control room an alarm countdown is triggered and you have only a limited number of rounds before the alarm goes off. Once the alarm goes off, Rick is doomed…

Rick will die if any of the following happens:

  • Rick’s jetpack runs out of fuel. You have enough fuel for 1200 movements.
  • Rick does not reach the starting position before the alarm goes off. The alarm countdown is triggered once the control room has been reached.
  • Rick touches a wall or the ground: he is ripped apart by a mechanical trap.

You will be successful if you reach the control room and get back to the starting position before the alarm goes off.

Maze format
A maze in ASCII format is provided as input. The character #  represents a wall, the letter . represents a hollow space, the letter T represents your starting position, the letter C represents the control room and the character ? represents a cell that you have not scanned yet. For example:

??????????????????????????????
#..............???????????????
#.#############???????????????
#.....T........???????????????
#.......................#.#..#
#.#######################.#..#
#.....##......##......#....###
#...####..##..##..##..#..#...#
#.........##......##.....#.C.#
##############################

Starting position is at row 3, column 6.
Control room is at row 8, column 27.
(Indexes start at zero)


! Note
The tests provided are similar to the validation tests used to compute the final score but remain different.


Game Input:

The program must first read the initialization data from standard input. Then, within an infinite loop, read the data from the standard input related to the maze’s current state and provide to the standard output the next movement instruction.

Initialization input:
Line 1: 3 integers: R C A
R,C are the numbers of rows and columns of the maze.
A, is the number of rounds between the time the alarm countdown is activated and the time the alarm goes off.

Input for one game turn:
Line 1: 2 integers: KR and KC. Rick is located at row KR and column KC within the maze. The cell at row 0 and column 0 is located in the top left corner of the maze.
Next R lines: C characters  # or  . or  T or  C or  ? (i.e. one line of the ASCII maze)

Output for one game turn:

single line containing one of: UP DOWN LEFT or RIGHT

Constraints:
10 ≤ R ≤ 100
20 ≤ C ≤ 200
1 ≤ A ≤ 100
0 ≤ KR < R
0 ≤ KC < C
Response time per turn ≤ 150ms
There at a single T character and a single C character within a maze


Example:

Initialization Input

10 30 23

Maze has 10 rows and 30 columns.
Alarm count down is 23.

No output expected


Input for Turn 1

3 6
??????????????????????????????
????.....?????????????????????
????#####?????????????????????
????..T..?????????????????????
????.....?????????????????????
????#####?????????????????????
??????????????????????????????
??????????????????????????????
??????????????????????????????
??????????????????????????????

Rick starts at row 3, column 6.
Only 25 cells are visible.

Output for turn 1

RIGHT

Rick moves to the right


Input for turn 2

3 7
??????????????????????????????
????......????????????????????
????######????????????????????
????..T...????????????????????
????......????????????????????
????######????????????????????
??????????????????????????????
??????????????????????????????
??????????????????????????????
??????????????????????????????

Rick starts at row 3, column 7.
Now 30 cells are visible.

Output for turn 2

RIGHT

Rick moves to the right


Link to the puzzle: https://www.codingame.com/training/hard/the-labyrinth

using System;
using System.Linq;
using System.IO;
using System.Text;
using System.Collections;
using System.Collections.Generic;

/**
 * Auto-generated code below aims at helping you parse
 * the standard input according to the problem statement.
 **/
class Player
{
    // Maze Legend
    const string UNDISCOVERED = "?";
    const string KURT = "@";
    const string WALL = "#";
    const string START = "T";
    const string CONTROL = "C";
    
    static void Main(string[] args)
    {
        string[] inputs;
        inputs = Console.ReadLine().Split(' ');
        int R = int.Parse(inputs[0]); // number of rows.
        int C = int.Parse(inputs[1]); // number of columns.
        int A = int.Parse(inputs[2]); // number of rounds between the time the alarm countdown is activated and the time the alarm goes off.
        Tile[,] maze = new Tile[C,R]; // map for debugging
        int xControl = -1;
        int yControl = -1;
        Queue<string> instructionQueue = new Queue<string>();
        Queue<string> tempQueue = new Queue<string>();
        bool controlFound = false;
        bool controlReachable;
        bool controlActivated = false;
        for (int i=0;i<R*C;i++) // initilize the map with 'X'
        {
            maze[i%C,i/C] = new Tile(i%C, i/C, UNDISCOVERED);
        }
        // game loop
        while (true)
        {
            inputs = Console.ReadLine().Split(' ');
            int KR = int.Parse(inputs[0]); // row where Kirk is located.
            int KC = int.Parse(inputs[1]); // column where Kirk is located.
            for (int i = 0; i < R; i++)
            {
                int col = 0;
                string ROW = Console.ReadLine(); // C of the characters in '#.TC?' (i.e. one line of the ASCII maze).
                if (IsInView(i, KR))
                {
                    for(int j = 0; j < C; j++)
                    {
                        if(IsInView(j, KC))
                        {
                            maze[j,i].Type = ROW[j].ToString();
                            if (maze[j,i].Type == CONTROL)
                            {
                                xControl = j;
                                yControl = i;
                                controlFound = true;
                            }
                        }
                    }
                }
            }
            maze[KC,KR].Type = KURT;
            DisplayMaze(maze, R, C);
            if (instructionQueue.Count == 0)
            {
                if(controlActivated) //control activated, go back to start
                {
                    SearchShortestPath(KC, KR, START, maze, ref instructionQueue);
                }
                else if(controlFound)
                {
                    controlReachable = SearchShortestPath(KC, KR, CONTROL, maze, ref instructionQueue); //check is there is a path to the control room
                    Console.Error.WriteLine("ControlReachable = {0}", controlReachable);
                    if(controlReachable)
                    {
                        SearchShortestPath(xControl, yControl, START, maze, ref tempQueue); //if the control is reachable, check the path back from the control to the start
                        Console.Error.WriteLine("Dist from Ctl to Start is {0}, Alarm needs {1}", tempQueue.Count, A);
                        if(tempQueue.Count > A) //is the path back to the start is longer than the time needed to raise the alarm, go back to the start, else discover more of the map to search for a shorter path
                        {
                            Console.Error.WriteLine("Too Far");
                            SearchShortestPath(KC, KR, UNDISCOVERED, maze, ref instructionQueue);
                        }
                        else
                        {
                            Console.Error.WriteLine("Reachable");
                            controlActivated = true;
                        }
                    }
                    else //if the control isnt reachable, discover more of the maze
                    {
                        SearchShortestPath(KC, KR, UNDISCOVERED, maze, ref instructionQueue);
                    }
                }
                else //discover the closest unknown tile
                {
                    SearchShortestPath(KC, KR, UNDISCOVERED, maze, ref instructionQueue);
                }
                
            }
            Console.Error.WriteLine(instructionQueue.Count);
            Console.WriteLine(instructionQueue.Dequeue());
            // Write an action using Console.WriteLine()
            // To debug: Console.Error.WriteLine("Debug messages...");

             // Kirk's next move (UP DOWN LEFT or RIGHT).
        }
    }
    
    static bool SearchShortestPath(int xOrigin, int yOrigin, string destinationType, Tile[,] maze, ref Queue<string> instructionQueue) //simple BFS on the 4 directions around the active tile
    {
        bool found = false;
        List<Tile> toTest = new List<Tile>();
        toTest.Add(maze[xOrigin, yOrigin]);
        List<Tile> visited = new List<Tile>(); 
        Tile targetTile = null;
        
        while (toTest.Count != 0 & !found)
        {   List<Tile> tempTile = new List<Tile>();
            foreach (Tile todo in toTest)
            {
                Tile up = maze[todo.Coordinates.Item1, todo.Coordinates.Item2-1];
                Tile down = maze[todo.Coordinates.Item1, todo.Coordinates.Item2+1];
                Tile left = maze[todo.Coordinates.Item1-1, todo.Coordinates.Item2];
                Tile right = maze[todo.Coordinates.Item1+1, todo.Coordinates.Item2];
                if(!toTest.Contains(right) && !tempTile.Contains(right) && !visited.Contains(right) && !found)
                {
                    right.SetInstructionsValues(todo.GetInstructions());
                    
                    if (right.Type != WALL && right.Type != UNDISCOVERED && (right.Type != CONTROL | destinationType == CONTROL))
                    {
                        right.AddDirection("RIGHT");
                    }
                    if(right.Type == destinationType)
                    {
                        targetTile = right;
                        found = true;
                    }
                    else if (right.Type != WALL && right.Type != UNDISCOVERED && (right.Type != CONTROL | destinationType == CONTROL))
                    {
                        tempTile.Add(right);
                    }
                    
                }
                if(!toTest.Contains(left) && !tempTile.Contains(left) && !visited.Contains(left) && !found)
                {
                    left.SetInstructionsValues(todo.GetInstructions());
                    if (left.Type != WALL && left.Type != UNDISCOVERED && (left.Type != CONTROL | destinationType == CONTROL))
                    {
                        left.AddDirection("LEFT");
                    }
                    if(left.Type == destinationType)
                    {
                        targetTile = left;
                        found = true;
                    }
                    else if (left.Type != WALL && left.Type != UNDISCOVERED && (left.Type != CONTROL | destinationType == CONTROL))
                    {
                        tempTile.Add(left);
                    }
                    
                }
                if(!toTest.Contains(up) && !tempTile.Contains(up) && !visited.Contains(up) && !found)
                {
                    up.SetInstructionsValues(todo.GetInstructions());
                    if (up.Type != WALL && up.Type != UNDISCOVERED && (up.Type != CONTROL | destinationType == CONTROL))
                    {
                        up.AddDirection("UP");
                    }
                    if(up.Type == destinationType)
                    {
                        targetTile = up;
                        found = true;
                    }
                    else if (up.Type != WALL && up.Type != UNDISCOVERED && (up.Type != CONTROL | destinationType == CONTROL))
                    {
                        tempTile.Add(up);
                    }
                    
                }
                if(!toTest.Contains(down) && !tempTile.Contains(down) && !visited.Contains(down) && !found)
                {
                    down.SetInstructionsValues(todo.GetInstructions());
                    if (down.Type != WALL && down.Type != UNDISCOVERED && (down.Type != CONTROL | destinationType == CONTROL))
                    {
                        down.AddDirection("DOWN");
                    }
                    if(down.Type == destinationType)
                    {
                        targetTile = down;
                        found = true;
                    }
                    else if (down.Type != WALL && down.Type != UNDISCOVERED && (down.Type != CONTROL | destinationType == CONTROL))
                    {
                        tempTile.Add(down);
                    }
                }
                todo.FlushInstructions(); //resets the path to the current since this is not the one searched.
            }
            visited = toTest;
            toTest = tempTile;
        }
        if (found == true)
        {
            instructionQueue = new Queue<string>(targetTile.GetInstructions());
            targetTile.FlushInstructions();
        }
        foreach (Tile todo in toTest)
        {
            todo.FlushInstructions(); //resets the path to the current since this is not the one searched.
        }
        return found;
    }
    
    static void DisplayMaze(Tile[,] maze,int xLength , int yLength) //Display the map in the error out. Debug function
    {
        for (int i=0;i<xLength;i++)
        {
            string xString = "";
            for (int j=0; j<yLength; j++)
            {
                xString += maze[j,i].Type;
            }
            Console.Error.WriteLine(xString);
        }
    }
    
    static bool IsInView(int inRow, int kurtRow) //return true of the tile is "in view" of kurt (meaning 2 tiles distance from him)
    {
        return (inRow == kurtRow-2) |  (inRow == kurtRow-1) |  (inRow == kurtRow) |  (inRow == kurtRow+1) |  (inRow == kurtRow+2);
    }
}

class Tile
{
    private Tuple<int, int> _coord; //coordinates of the tile
    private string _type = ""; //type of the tile (see the legend at the top of the player class)
    private Queue<string> _dirInstructions = new Queue<string>(); //queue of the instructions to reach the tile from the current start position
    
    public Tuple<int, int> Coordinates { get { return _coord; } }
    public string Type { get {return _type;} set {_type = value;} }
    
    public Tile(int x, int y, string type) //constructor for tile
    {
        _coord = new Tuple<int, int>(x, y);
        _type = type;
    }
    
    public void AddDirection(string dir) //enqueue a direction in the instruction queue
    {
        _dirInstructions.Enqueue(dir);
    }
    
    public Queue<string> GetInstructions() //returns the instruction queue
    {
        return _dirInstructions;
    }
    
    public void FlushInstructions() //removes all instructions in the queue
    {
        _dirInstructions.Clear();
    }
    
    public void SetInstructionsValues(Queue<string> qdir) //replace the currentinstruction queu from the object by qdi
    {
        this.FlushInstructions();
        _dirInstructions = new Queue<string> (qdir);
    }
    
    public override string ToString() //override ToString() for debugging purpose
    {
        return _coord.ToString() + ":" + _type + "<" + _dirInstructions.Count.ToString(); 
    }
    
    //OVERRIDE FOR EQUALITY
    public override bool Equals(System.Object obj) // Needed to override '=='
    {
        if (obj == null) // If parameter is null return false.
        {
            return false;
        }
        Tile t = obj as Tile;
        if ((System.Object)t == null) // If parameter cannot be cast to Point return false.
        {
            return false;
        }
        return _coord.Equals(t.Coordinates); // Could include type, buit as it is supposed to change, letÄs not over complicate it
    }
    
    public override int GetHashCode() // Needed to override '=='
    {
        return _coord.GetHashCode();
    }
    
    public static bool operator == (Tile a, Tile b) //overrides == between 2 tiles
    {
        if (System.Object.ReferenceEquals(a, b)) // If both are null, or both are same instance, return true.
        {
            return true;
        }
        if (((object)a == null) || ((object)b == null)) // If one is null, but not both, return false.
        {
            return false;
        }
        return a.Coordinates.Equals(b.Coordinates); // Return true if the coordinates match:
    }
    
    public static bool operator !=(Tile a, Tile b) //override != between 2 tiles
    {
        return !(a == b);
    }
}

Mon profil

Mon profil est consultable ici: https://www.codingame.com/profile/200ea5528cea62e2d81d6f4cf16b84ee3953021

Dans un soucis de sauvegarde, vous en trouverez une capture d’écran ci-dessous. Les certification déliverées par le site sont consultables directement sur mon profil ou en Annexe 5.6.