Julia. Les premiers pas, septième partie

using Dates

let # portée locale
    d = Dates.now()
    println("Daniel Hagnoul $d\n")
end
Daniel Hagnoul 2020-11-04T16:29:48.732

Conception de fonctions

Les fonctions sont des éléments fondamentaux du code.

Nous allons coder quelques éléments d'un jeu d'astéroïdes. Notre but n'est pas le jeu mais la construction et l'exécution de diverses fonctions.

J'ai volontairement largement utilisé le français pour les variables et les fonctions, Julia n'a pas de problèmes avec l'utf-8.

mutable struct Position
    x::Int
    y::Int
end

struct Taille
    largeur::Int
    hauteur::Int
end

#=
    Une chose est n'importe quoi qui existe dans la galaxie.
    Les sous-types concrets doivent avoir au moins deux 
    propriétés : position et taille.
=#
abstract type Chose end

# Fonctions qui s'appliquent à toutes les choses
position(c::Chose) = c.position
taille(c::Chose) = c.taille
forme(c::Chose) = :unknown

# types d'armes
@enum Armes Laser Missile

struct VaisseauSpatial <: Chose
    nom::String
    position::Position
    taille::Taille
    armes::Armes
end

forme(v::VaisseauSpatial) = :soucoupe_volante

struct Astéroïde <: Chose
    nom::String
    position::Position
    taille::Taille
end

struct Rectangle
    sommet::Int
    gauche::Int
    bas::Int
    droit::Int

    #=
        Retourne les points : sommet, gauche, bas et droit

        Pour créer un Rectangle on doit écrire
            mon_rect = Rectangle(Position(10, 20), Taille(30, 20)) 
        et pas 
            mon_rect = Rectangle(40, 10, 20, 40)
        
        new() : fonction spéciale utilisée pour les constructeurs 
        internes qui crée un nouvel objet du type
    =#
    Rectangle(p::Position, t::Taille) = new(p.y + t.hauteur, p.x, p.y, p.x + t.largeur)
end

# test s'il y a un chevauchement entre deux Rectangle
function chevauchement(A::Rectangle, B::Rectangle)
    return A.gauche < B.droit && A.droit > B.gauche && A.sommet > B.bas && A.bas < B.sommet
end

function collision(A::Chose, B::Chose)
    rectA = Rectangle(position(A), taille(A))
    rectB = Rectangle(position(B), taille(B))
    return chevauchement(rectA, rectB)
end

# Le point d'exclamation indique que la Chose sera modifiée.
déplacement_en_haut!(c::Chose, v::Int) = c.position.y -= v
déplacement_en_bas!(c::Chose, v::Int) = c.position.y += v
déplacement_à_gauche!(c::Chose, v::Int) = c.position.x -= v
déplacement_à_droite!(c::Chose, v::Int) = c.position.x += v

ast1 = Astéroïde("Véga", Position(0, 0), Taille(10, 20))

vs1 = VaisseauSpatial("Entreprise 1", Position(0, 0), Taille(30, 5), Missile)
vs2 = VaisseauSpatial("Entreprise 2", Position(10, 0), Taille(30, 5), Laser)

println("Position du vaisseau spatial $(vs1.nom) = $(position(vs1))\n")

déplacement_en_bas!(ast1, 30)

println("Position de l'astéroïde $(ast1.nom) = $(position(ast1))\n")

println("Forme du vaisseau spatial $(vs2.nom) = $(forme(vs2))\n")

println("Teste s'il y a collision entre $(vs1.nom) et $(vs2.nom) = $(collision(vs1, vs2))\n")
Position du vaisseau spatial Entreprise 1 = Main.##WeaveSandBox#253.Positio
n(0, 0)

Position de l'astéroïde Véga = Main.##WeaveSandBox#253.Position(0, 30)

Forme du vaisseau spatial Entreprise 2 = soucoupe_volante

Teste s'il y a collision entre Entreprise 1 et Entreprise 2 = true
#=
   Utilisation de mots-clés comme arguments. 
   
   Les arguments de position (dans ce cas, n) et les arguments de 
   mots-clés doivent être séparés par le caractère point-virgule.

   L'avantage, avoir des valeurs par défaut (ce n'est pas obligatoire) 
   et ne pas devoir se rappeler de l'ordre d'insertion des mots-clés 
   lors de l'appel de la fonction.
=#

function fabrique_astéroïde(n::Int ; nom = "Astéroïde", pos_range = 0:200, size_range = 10:30)
    pos_rand() = rand(pos_range)
    size_rand() = rand(size_range)
    return [Astéroïde("$nom #$i", Position(pos_rand(), pos_rand()), Taille(size_rand(), size_rand())) for i in 1:n]
end

function fabrique_vaisseau_spatial(n::Int ; nom = "Vaisseau_spatial", pos_range = 0:200, size_range = 10:30, arme::Armes = Missile)
    pos_rand() = rand(pos_range)
    size_rand() = rand(size_range)
    return [VaisseauSpatial("$nom #$i", Position(pos_rand(), pos_rand()), Taille(size_rand(), size_rand()), arme) for i in 1:n]
end

# Chose... : slurping arguments, aspirer les arguments pour créer un Array

function tirer(origine::Chose, cibles::Chose...)
    println("type des cibles = $(typeof(cibles))\n")
    for cible in cibles
        println("$(origine.nom) abat $(cible.nom)")
    end
end

mes_astéroïdes = fabrique_astéroïde(5)
mes_vaisseaux = fabrique_vaisseau_spatial(3)

target1 = mes_astéroïdes[1]
target2 = mes_astéroïdes[2]
target3 = mes_astéroïdes[3]

tirer(mes_vaisseaux[1], target2, target1, target3)

println()

# mes_astéroïdes[2:4]... : splatting arguments, exploser l'Array pour obtenir les arguments

tirer(mes_vaisseaux[3], mes_astéroïdes[4:5]...)
type des cibles = Tuple{Main.##WeaveSandBox#253.Astéroïde,Main.##WeaveSandB
ox#253.Astéroïde,Main.##WeaveSandBox#253.Astéroïde}

Vaisseau_spatial #1 abat Astéroïde #2
Vaisseau_spatial #1 abat Astéroïde #1
Vaisseau_spatial #1 abat Astéroïde #3

type des cibles = Tuple{Main.##WeaveSandBox#253.Astéroïde,Main.##WeaveSandB
ox#253.Astéroïde}

Vaisseau_spatial #3 abat Astéroïde #4
Vaisseau_spatial #3 abat Astéroïde #5
# retourne, au hasard, une fonction déplacement
function déplacement_aléatoire()
    return rand([déplacement_en_bas!, déplacement_à_gauche!, déplacement_à_droite!, déplacement_en_haut!])
end

function saut_aléatoire!(c::Chose, déplacement_fonction::Function, distance::Int)
    déplacement_fonction(c, distance)
    return c
end

println("mes_vaisseaux[1] = $(mes_vaisseaux[1])\n")

saut_aléatoire!(mes_vaisseaux[1], déplacement_aléatoire(), 25)
mes_vaisseaux[1] = Main.##WeaveSandBox#253.VaisseauSpatial("Vaisseau_spatia
l #1", Main.##WeaveSandBox#253.Position(103, 86), Main.##WeaveSandBox#253.T
aille(29, 22), Main.##WeaveSandBox#253.Missile)

Main.##WeaveSandBox#253.VaisseauSpatial("Vaisseau_spatial #1", Main.##Weave
SandBox#253.Position(103, 61), Main.##WeaveSandBox#253.Taille(29, 22), Main
.##WeaveSandBox#253.Missile)
intact(s::VaisseauSpatial) = true # true pour le test mais normalement rand(Bool)

function feu(f::Function, s::VaisseauSpatial)
    if intact(s)
        f(s)
    else
        println("Opération annulée car le vaisseau n'est pas intact")
    end
    return nothing
end

println("mes_vaisseaux[2] = $(mes_vaisseaux[2])\n")

# fonction anonyme do-block

feu(mes_vaisseaux[2]) do s 
    déplacement_en_haut!(s, 100)
    println("$(s), missile tiré")
    déplacement_en_bas!(s, 100)
end
mes_vaisseaux[2] = Main.##WeaveSandBox#253.VaisseauSpatial("Vaisseau_spatia
l #2", Main.##WeaveSandBox#253.Position(194, 134), Main.##WeaveSandBox#253.
Taille(20, 24), Main.##WeaveSandBox#253.Missile)

Main.##WeaveSandBox#253.VaisseauSpatial("Vaisseau_spatial #2", Main.##Weave
SandBox#253.Position(194, 34), Main.##WeaveSandBox#253.Taille(20, 24), Main
.##WeaveSandBox#253.Missile), missile tiré
# fonction anonyme x ->

function clean_up_galaxy(astéroïdes, vaisseaux)
    f = x -> println("explosion de $(x)")
    foreach(f, astéroïdes)
    foreach(f, vaisseaux)
end

clean_up_galaxy(mes_astéroïdes, mes_vaisseaux)
explosion de Main.##WeaveSandBox#253.Astéroïde("Astéroïde #1", Main.##Weave
SandBox#253.Position(143, 155), Main.##WeaveSandBox#253.Taille(14, 11))
explosion de Main.##WeaveSandBox#253.Astéroïde("Astéroïde #2", Main.##Weave
SandBox#253.Position(151, 189), Main.##WeaveSandBox#253.Taille(27, 12))
explosion de Main.##WeaveSandBox#253.Astéroïde("Astéroïde #3", Main.##Weave
SandBox#253.Position(196, 169), Main.##WeaveSandBox#253.Taille(11, 13))
explosion de Main.##WeaveSandBox#253.Astéroïde("Astéroïde #4", Main.##Weave
SandBox#253.Position(6, 140), Main.##WeaveSandBox#253.Taille(28, 20))
explosion de Main.##WeaveSandBox#253.Astéroïde("Astéroïde #5", Main.##Weave
SandBox#253.Position(77, 88), Main.##WeaveSandBox#253.Taille(12, 22))
explosion de Main.##WeaveSandBox#253.VaisseauSpatial("Vaisseau_spatial #1",
 Main.##WeaveSandBox#253.Position(103, 61), Main.##WeaveSandBox#253.Taille(
29, 22), Main.##WeaveSandBox#253.Missile)
explosion de Main.##WeaveSandBox#253.VaisseauSpatial("Vaisseau_spatial #2",
 Main.##WeaveSandBox#253.Position(194, 134), Main.##WeaveSandBox#253.Taille
(20, 24), Main.##WeaveSandBox#253.Missile)
explosion de Main.##WeaveSandBox#253.VaisseauSpatial("Vaisseau_spatial #3",
 Main.##WeaveSandBox#253.Position(14, 126), Main.##WeaveSandBox#253.Taille(
25, 10), Main.##WeaveSandBox#253.Missile)

Licence Creative Commons Attribution 2.0 Belgique