Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
anonymax25 committed Dec 5, 2021
0 parents commit 101eac8
Showing 17 changed files with 887 additions and 0 deletions.
Binary file added __MACOSX/projet/._sujet.html
Binary file not shown.
4 changes: 4 additions & 0 deletions projet/funprog-al/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
target/
project/target
project/project/target
.bsp
16 changes: 16 additions & 0 deletions projet/funprog-al/.scalafmt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version=2.2.1

style = default

#
# Formatting
docstrings = JavaDoc

# Alignment
align = some
align.tokens = ["%", "%%", "->", { code = "<-", owner = "Enumerator.Generator" }, { code = "=>", owner = "Case" }]
danglingParentheses = true

#
# Rewrite rules
rewrite.rules = [SortImports]
138 changes: 138 additions & 0 deletions projet/funprog-al/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Projet AL

## Pré-requis

Il est indispensable d'avoir installé en local:

- la version 2.13 du compilateur Scala, [ici](https://scala-lang.org/download/)

- le gestionnaire de build `sbt`, [voir ici](https://www.scala-sbt.org/download.html). En installant `sbt`, le compilateur sera installé aussi.

## Structure du projet

Ceci est le projet de validation du cours d'initiation à la programmation fonctionnelle. Le code source doit être écrit dans le répertoire `./src/main/scala`. Vous pourrez créer autant de package que vous voulez.
Les tests unitaires doivent être écrit dans le répertoire `./src/test/scala`. Pour écrire des tests unitaires, veuillez vous reporter à la section [Tests Unitaires](#tests-unitaires).

## Guide de survie avec sbt

Ce projet est un application Scala standalone. Il est géré par `sbt`, le build tool Scala. Sa documentation est disponible [ici](https://www.scala-sbt.org/1.x/docs/).

Nous allons lister ici une liste de commandes utiles avec `sbt`:

- `sbt`: cette commande lance un invite de commande interactif

- `run` (ou `sbt run` hors de l'invite de commande): lance la classe `Main` du projet `sbt`

- `compile` (ou `sbt compile` hors de l'invite de commande): lance la compilation de l'ensemble du projet `sbt` (compile toutes les classes)

- `console` (`sbt console` hors de l'invite de commande): lance un REPL interactif Scala. Les dépendances du projet `sbt` seront disponibles et pourront être importés.

## Manipulation de fichiers

Nous allons voir ici quelques commandes pour vous aider avec la manipulation de fichiers en `Scala`.

Pour lire un fichier nous pouvons le faire comme suit (en utilisant la lib [better-files](https://github.com/pathikrit/better-files)):

```scala
import better.files._

val f = File("/User/johndoe/Documents") // using constructor

// on va récupérer toutes les lignes du fichier
f.lines.toList

// si on veut récupérer tout le contenu du fichier en String
f.contentAsString
```

Pour écrire dans un fichier, nous pouvons le faire ainsi:

```scala
import better.files._

val f = File("/User/johndoe/Documents") // using constructor

// pour ajouter du contenu dans un fichier ligne par ligne
f.createIfNotExists()
.appendLine() // on rajoute une ligne vide
.appendLines("My name is", "Inigo Montoya") // on ajoute 2 nouvelles lignes

// pour écraser le contenu du fichier
f.createIfNotExists().overwrite("hello")
```

## Tests unitaires

Il est possible de lancer tous les tests du projets avec la commande: `sbt test` (ou `test` si on est dans l'invite de commande `sbt`).

Pour créer une classe de test, il suffit de créer une classe étendant `org.scalatest.funsuite.AnyFunSuite`:

```scala

import org.scalatest.funsuite.AnyFunSuite

class HelloSpec extends AnyFunSuite {}
```

Les tests devant être lancés doivent être placés dans le corps de la classe. Pour créer un test, il suffit d'appeler `test` en lui passant un nom de test et le code de test à effectuer comme ceci:

```scala
import org.scalatest.funsuite.AnyFunSuite

class HelloSpec extends AnyFunSuite {
test("The Hello object should say hello") {
assert(Hello.greeting === "hello")
}
}
```

Le test sera lancé dès lorsqu'on lancera la commande `test`:

```scala
sbt:funprog-AL> test
[info] Formatting 1 Scala sources...
[info] compiling 1 Scala source to ../projet/funprog-al/target/scala-2.13/test-classes ...
[info] HelloSpec:
[info] - The Hello object should say hello
[info] Run completed in 251 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 1 s, completed 14 nov. 2021 à 14:46:48
```

Une classe de test d'exemple vous est fourni dans `./src/test/example/HelloSpec.scala`.

## Lecture des fichiers de conf

La librairie [config](https://github.com/lightbend/config) a été rajouté au projet. Elle permet de lire les fichiers de configuration (au format `.conf`).
Un fichier de configuration a éta ajouté au projet (voir le fichier `./src/main/resources/application.conf`).

Voici un exemple d'utilisation de l'api `config` (tiré de la documentation officielle).

Pour le fichier `application.conf` suivant:

```conf
foo {
bar = 1
}
foo1.baz = "some texte"
foo1.baz = ${?FOO1_BAZ} # variable d'environnement pour sucharger la conf
```

La lecture du fichier de conf se fera comme suit:

```scala
import com.typesafe.config.{Config, ConfigFactory}

// Pour charger la configuration. `ConfigFactory#load` va chercher et lire le fichier `application.conf`.
val conf: Config = ConfigFactory.load()

// Une fois le fichier de conf chargé, on peut récupéré une valeur par sa clé (ex: `foo.bar`).
val bar1: Int = conf.getInt("foo.bar")
val foo: Config = conf.getConfig("foo")
val bar2: Int = foo.getInt("bar")
val baz: String = conf.getString("foo1.baz")
```
74 changes: 74 additions & 0 deletions projet/funprog-al/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import Dependencies._

ThisBuild / scalaVersion := "2.13.3"
ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / organization := "com.example"
ThisBuild / organizationName := "example"
// Scalafmt
ThisBuild / scalafmtOnCompile := true

fork in run := true
connectInput in run := true
mainClass in Compile := Some("fr.esgi.al.funprog.Main")

lazy val root = (project in file("."))
.settings(
name := "funprog-AL",
scalacOptions ++= compilerOptions,
libraryDependencies ++= Seq(
betterFiles,
typesafeConfig,
playJson,
scalatic,
scalaTest % Test
),
// Wartremover
wartremoverWarnings ++= Warts.unsafe,
wartremoverErrors ++= Warts.unsafe
)


val compilerOptions = Seq(
// Common settings
//
"-deprecation", // Emit warning and location for usages of deprecated APIs.
"-encoding",
"utf-8", // Specify character encoding used by source files.
"-explaintypes", // Explain type errors in more detail.
"-feature", // Emit warning and location for usages of features that should be imported explicitly.
"-unchecked", // Enable additional warnings where generated code depends on assumptions.
"-Xcheckinit", // Wrap field accessors to throw an exception on uninitialized access.
"-Werror", // Fail the compilation if there are any warnings.
"-Ymacro-annotations",
// Warning settings
//
// "-Wself-implicit",
"-Wdead-code",
"-Wextra-implicit",
"-Wnumeric-widen",
"-Wvalue-discard",
"-Wunused:imports,patvars,privates,locals,explicits,implicits,params,linted",
// Linting
//
"-Xlint:adapted-args",
"-Xlint:inaccessible", // Warn about inaccessible types in method signatures.
"-Xlint:infer-any", // Warn when a type argument is inferred to be `Any`.
"-Xlint:missing-interpolator",
"-Xlint:doc-detached",
"-Xlint:private-shadow",
"-Xlint:type-parameter-shadow",
"-Xlint:poly-implicit-overload",
"-Xlint:option-implicit",
"-Xlint:package-object-classes",
"-Xlint:stars-align",
"-Xlint:constant",
"-Xlint:nonlocal-return",
"-Xlint:valpattern",
"-Xlint:eta-zero,eta-sam",
"-Xlint:deprecation",
"-Xlint:nullary-unit", // Warn when nullary methods return Unit.
"-Ywarn-value-discard", // Warn when non-Unit expression results are unused.
"-Xfatal-warnings"
)

val consoleOptions = Seq()
Binary file added projet/funprog-al/project/.DS_Store
Binary file not shown.
9 changes: 9 additions & 0 deletions projet/funprog-al/project/Dependencies.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import sbt._

object Dependencies {
lazy val betterFiles = "com.github.pathikrit" %% "better-files" % "3.9.1"
lazy val typesafeConfig = "com.typesafe" % "config" % "1.4.1"
lazy val playJson = "com.typesafe.play" %% "play-json" % "2.9.1"
lazy val scalatic = "org.scalactic" %% "scalactic" % "3.2.0"
lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.2.2"
}
1 change: 1 addition & 0 deletions projet/funprog-al/project/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version=1.4.1
2 changes: 2 additions & 0 deletions projet/funprog-al/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.3.2")
addSbtPlugin("org.wartremover" % "sbt-wartremover" % "2.4.13")
25 changes: 25 additions & 0 deletions projet/funprog-al/src/main/resources/application.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
appplication {
name = "FunProg"
name = ${?APP_NAME} # Pour surcharger la valeur par un variable d'environnement

input-file = "/tmp/input.txt"
input-file = ${?INPUT_FILE}

output-json-file = "/tmp/output.json"
output-json-file = ${?OUTPUT_JSON_FILE}

output-csv-file = "/tmp/output.csv"
output-csv-file = ${?OUTPUT_CSV_FILE}
}

# A supprimer
example {
message = "hi buddy"
message = ${?EXAMPLE_MESSAGE}

one = 1
one = ${?EXAMPLE_ONE}

yes = "true"
yes = ${?EXAMPLE_YES}
}
32 changes: 32 additions & 0 deletions projet/funprog-al/src/main/scala/example/Hello.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package example

import scala.io.StdIn

import com.typesafe.config.{Config, ConfigFactory}

// Lancer l'exemple avec: sbt "runMain example.Hello"
// TODO: a supprimer
object Hello extends Greeting with App {
println(":> Enter your name: ")
val user = StdIn.readLine()
println(s":> $greeting $user")

val conf: Config = ConfigFactory.load()
val message: String = conf.getString("example.message")
println(s":> message: $message")
val one: Int = conf.getInt("example.one")
println(s":> one: ${one.toString}")
val yes: Boolean = conf.getBoolean("example.yes")
println(s":> yes: ${yes.toString}")
}

trait Greeting {
lazy val greeting: String = "hello"

case class MyException(msg: String) extends Exception

@SuppressWarnings(Array("org.wartremover.warts.Throw"))
def dangerous(): Unit = {
throw new MyException("boooom")
}
}
10 changes: 10 additions & 0 deletions projet/funprog-al/src/main/scala/progfun/Main.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package fr.esgi.al.funprog

object Main extends App {
println("Ici le programme principal")
// Le code suivant ne compilera pas.
// var tmp = null;
// var tmp2 = if (tmp == 1) "yes" else 1

// println(s"tmp: $tmp, tmp2: $tmp2")
}
21 changes: 21 additions & 0 deletions projet/funprog-al/src/test/scala/example/HelloSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package example

import org.scalatest.funsuite.AnyFunSuite

class HelloSpec extends AnyFunSuite {

test("The Hello object should say hello") {
assert(Hello.greeting === "hello")
}

test("Hello size is equals to 5") {
assert(Hello.greeting.size === 5)
}

test(
""" "Hello"(6) should throw a "java.lang.StringIndexOutOfBoundsException" """
) {
assertThrows[java.lang.StringIndexOutOfBoundsException]("Hello" (6))
}

}
15 changes: 15 additions & 0 deletions projet/funprog-al/src/test/scala/example/StringSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package example

import org.scalatest.funsuite.AnyFunSuite

class StringSpec extends AnyFunSuite {

test("CHaine vide est vide") {
assert("" == "")
}

test("Chaine ") {

}

}
359 changes: 359 additions & 0 deletions projet/sujet.html

Large diffs are not rendered by default.

181 changes: 181 additions & 0 deletions projet/sujet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# Projet

## Contexte

La société FunProg a décidé de développer une tondeuse à gazon automatique, destinée aux surfaces rectangulaires.

La tondeuse peut être programmée pour parcourir l'intégralité de la surface. La position de la tondeuse est représentée par une combinaison de coordonnées (x,y) et d'une lettre indiquant l'orientation selon la notation cardinale anglaise (N,E,W,S). La pelouse est divisée en grille pour simplifier la navigation. Par exemple, la position de la tondeuse peut être « 0, 0, N », ce qui signifie qu'elle se situe dans le coin inférieur gauche de la pelouse, et orientée vers le Nord.

Pour contrôler la tondeuse, on lui envoie une séquence simple de lettres. Les lettres possibles sont « D », « G » et « A ». « D » et « G » font pivoter la tondeuse de 90° à droite ou à gauche respectivement, sans la déplacer. « A » signifie que l'on avance la tondeuse d'une case dans la direction à laquelle elle fait face, et sans modifier son orientation. Si la position après mouvement est en dehors de la pelouse, la tondeuse ne bouge pas, conserve son orientation et traite la commande suivante. On assume que les cases directement au Nord et au Sud de la position (x, y) ont pour coordonnées respectivement (x, y+1) et (x, y-1).

Pour programmer la tondeuse, on lui fournit un fichier d'entrée construit comme suit :
- La première ligne correspond aux coordonnées du coin supérieur droit de la pelouse, celles du coin inférieur gauche sont supposées être (0,0)
- La suite du fichier permet de piloter toutes les tondeuses qui ont été déployées. Chaque tondeuse a deux lignes la concernant :
- la première ligne donne la position initiale de la tondeuse, ainsi que son orientation. La position et l'orientation sont fournies sous la forme de 2 chiffres et une lettre, séparés par un espace
- la seconde ligne est une série d'instructions ordonnant à la tondeuse d'explorer la pelouse. Les instructions sont une suite de caractères sans espaces.

Chaque tondeuse se déplace de façon séquentielle, ce qui signifie que la seconde tondeuse ne bouge que lorsque la première a exécuté intégralement sa série d'instructions. Lorsqu'une tondeuse achève une série d'instruction, elle communique sa position et son orientation. Le programme devra être en mesure de marcher avec un nombre non fixe de tondeuses. A la fin de l'exécution du programme, le résultat de l'exécution de toutes les tondeuses sera exportée en format json.

## Objectifs et consignes

L'objectif de ce projet sera de concevoir et écrire un programme `Scala` implémentant la spécification ci-dessus.

De plus le dit porgramme devra respecter les consignes suivantes (`Zéro` sera donné en cas de non-respect de ses consignes):

- il est **INTERDIT** d'utiliser le mot clé `return`
- il est **INTERDIT** d'utiliser le mot-clé `while`
- il est **INTERDIT** d'utiliser le mot-clé `null`
- il est **INTERDIT** d'utiliser les expressions régulières
- tout `if` devra être **exaustif** (c-à-d avec un `else`)
- tout `pattern-matching` devra être **exaustif** (avec un cas par défaut sinon, ou sans warning si pas besoin de cas par défaut - avec `collect` par exemple - )
- la mutabilité explicite (`var`) ou implicite (struture de données mutables du package `scala.collection.mutable` comme `ArrayBuffer`) est **INTERDITE**
- seules les implémentations valides (qui compilent) seront prises en compte et seules celles renvoyant un résultat correct donneront lieu à la totalité des points

Un template de projet `sbt` sera fourni. Celui-ci servira de base pour les devéloppements. Il n'est pas permis de changer les options de compilateurs définis pour ce projet. Lors du parsing des données d'entrées, si les données attendues ne sont pas fournies ou ne sont pas au format attendu, une exception de type `DonneesIncorectesException` (à créer) sera lancée avec un message d'erreur explicite. Il s'agit du seul type d'exception que le programme devra retourner. Veuillez bien à bien limiter les effets de bord aux frontières de votre programme (entrées/sorties) et non au coeur de celui-ci.

## Entrées/sorties et Tests

Pour un exemple où le fichier suivant est fourni en entrée :

```
5 5
1 2 N
GAGAGAGAA
3 3 E
AADAADADDA
```

Les 2 tondeuses devront respectivement se terminer avec les positions suivantes:

```
1 3 N
5 1 E
```

On attend le résultat suivant (position finale des tondeuses) à la fin de l'exécution du programme:

```json
{
"limite": {
"x": 5,
"y": 5
},
"tondeuses": [
{
"debut": {
"point": {
"x": 1,
"y": 2
},
"direction": "N"
},
"instructions": ["G","A","G","A","G","A","G","A","A"],
"fin": {
"point": {
"x": 1,
"y": 3
},
"direction": "N"
}
},
{
"debut": {
"point": {
"x": 3,
"y": 3
},
"direction": "E"
},
"instructions": ["A","A","D","A","A","D","A","D","D","A"],
"fin": {
"point": {
"x": 5,
"y": 1
},
"direction": "E"
}
}
]
}
```

## Version simplifiée

Cette section _simplifiée_ est pour les groupes de 2 personnes.

Pour la version simplifiée du programme (voir la section [Bonus](#bonus)), les données d'entrées pourront être fournies à votre guise: console (chaîne de caractères)ou fichier. De même le résultat de l'éxécution de votre programme pourra être fourni selon votre préférence: console, fichier de sortie ... Si les entrées ou sorties sont fournies par fichiers, alors veuillez bien à ce que cela puisse être configurable. Pour cela un fichier de configuration sera fourni et permettra de paramètrer le programme. Un template de fichier de configuration a été fourni et vous pourrez vous y référer (voir la section `Lecture des fichiers de conf` dans le `README` du projet).

Vous aurez dans le cadre de ce projet à définir des cas et données de tests qui permettront d'évaluer la correctitude de votre programme. Un guide pour écrire des tests automatisés (tests unitaires) sera fourni avec le projet.

Pour la sortie, en plus `json`, le programme devra en plus exporté sa sortie en `csv` et le chemin de la sortie devra être configurable. Vous aurez à déterminer la hiérarchie de `typeclasses` adéquates pour produire la sortie csv . L'utilisation de `typeclasses` est **OBLIGATOIRE** et la modélisation sera retenue dans la notation. Voici un exemple de la sortie pour l'exemple (de la section [Entrées/sorties et Tests](#entrées/sorties-et-tests)):

```csv
numéro;début_x;début_y;début_direction;fin_x;fin_y;fin_direction;instructions
1;1;2;N;1;3;N;GAGAGAGAA
2;3;3;E;5;1;E;AADAADADDA
```

## Bonus

Cette section _bonus_ est **OBLIGATOIRE** pour les groupes de 3 personnes. Ceci est un complément à la version simple.

Pour la sortie, en plus `json` et du `csv`, le programme devra en plus exporté sa sortie en `yaml` et le chemin de la sortie devra être configurable. Vous aurez aussi à déterminer la hiérarchie de `typeclasses` adéquates. L'utilisation de `typeclasses` est aussi **OBLIGATOIRE**. Voici un exemple de la sortie pour l'exemple (de la section [Entrées/sorties et Tests](#entrées/sorties-et-tests)):

```yaml
limite:
x: 5
y: 5
tondeuses:
- debut:
point:
x: 1
y: 2
direction: N
instructions:
- G
- A
- G
- A
- G
- A
- G
- A
- A
fin:
point:
x: 1
y: 3
direction: N
- debut:
point:
x: 3
y: 3
direction: E
instructions:
- A
- A
- D
- A
- A
- D
- A
- D
- D
- A
fin:
point:
x: 5
y: 1
direction: E
```
## Evaluation
Pour les groupes de 2 et de 3, un barème commun sera utilisé et celui-ci prendra en compte les différences entre les sujets. Le barème d'évaluation sera le suivant:
- parseur pour données d'entrée / 4pts
- modélisation du moteur de gestion de la grille et des tondeuses / 6pts
- récapitulatif final (position des tondeuses) en `json`, `csv` et `yaml` (pour le bonus) / 8pts
- pour les groupes de 2: `json`, `csv` (3pts et 5pts respectivement)
- pour les groupes de 3: `json`, `csv` et `yaml` (2pts, 2pts et 4pts respectivement)
- scénarios de test / 2pts
Binary file added projet/sujet.pdf
Binary file not shown.

0 comments on commit 101eac8

Please sign in to comment.