Files
SAE41_2024/src/main/java/com/example/flow_free/FlowFreeView.java
2025-03-30 22:02:58 +02:00

349 lines
13 KiB
Java
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.example.flow_free;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class FlowFreeView extends View {
private final Puzzle puzzle;
private final Paint paint = new Paint();
private final int[][] board;
private final Map<Integer, List<int[]>> paths = new HashMap<>();
private final Map<Integer, Integer> colorMap = new HashMap<>();
private final Set<Integer> completedColors = new HashSet<>();// liste des chemin fini
private boolean win = false;
private boolean isAchromate ;
private static final int[] DISTINCT_COLORS = {
Color.rgb(255, 0, 0), // Rouge vif
Color.rgb(0, 0, 255), // Bleu vif
Color.rgb(0, 255, 0), // Vert vif
Color.rgb(255, 255, 0), // Jaune vif
Color.rgb(255, 0, 255), // Fuchsia / Magenta
Color.rgb(0, 255, 255), // Cyan
Color.rgb(255, 165, 0), // Orange vif
Color.rgb(128, 0, 0), // Bordeaux
Color.rgb(0, 128, 0), // Vert foncé
Color.rgb(0, 0, 128), // Bleu foncé
Color.rgb(128, 128, 0), // Olive
Color.rgb(128, 0, 128), // Violet foncé
Color.rgb(0, 128, 128), // Teal foncé
Color.rgb(255, 105, 180), // Rose
Color.rgb(139, 69, 19), // Chocolat
Color.rgb(255, 20, 147), // Rose vif
Color.rgb(173, 255, 47), // Vert citron
Color.rgb(75, 0, 130), // Indigo profond
};
private static final int[] DISTINCT_GRIS = {
Color.rgb(0, 0, 0), // Noir
Color.rgb(40, 40, 40), // Gris très foncé
Color.rgb(80, 80, 80), // Gris foncé
Color.rgb(120, 120, 120), // Gris moyen foncé
Color.rgb(160, 160, 160), // Gris moyen
Color.rgb(200, 200, 200), // Gris moyen clair
Color.rgb(220, 220, 220), // Gris clair
Color.rgb(240, 240, 240), // Gris très clair
Color.rgb(255, 255, 255), // Blanc
Color.rgb(25, 25, 25),
Color.rgb(55, 55, 55),
Color.rgb(95, 95, 95),
Color.rgb(135, 135, 135),
Color.rgb(175, 175, 175)
};
private int colorId=0;
private int selectedColor = 0;
private int cellSize;
public FlowFreeView(Context context, Puzzle puzzle) {
super(context);
this.puzzle = puzzle;
this.board = new int[puzzle.getSize()][puzzle.getSize()];
// Initialisation du board avec les points du puzzle
for (int[] pair : puzzle.getPairs()) {
int col1 = pair[0];
int row1 = pair[1];
int col2 = pair[2];
int row2 = pair[3];
// Crée une couleur id unique pour chaque paire (1, 2, 3, ...)
colorId = colorId+ 1;
board[row1][col1] = colorId;
board[row2][col2] = colorId;
}
}
private void printBoardToLog() {
StringBuilder sb = new StringBuilder();
for (int[] row : board) {
for (int cell : row) {
sb.append(String.format("%3d", cell)).append(" ");
}
sb.append("\n");
}
}
@Override//test pour calculer cellsize
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (puzzle != null) {
cellSize = Math.min(w, h) / puzzle.getSize();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext());
isAchromate = preferences.getBoolean("achromate_mode", false);
paint.setStrokeWidth(4);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.BLACK);
// Affiche la grille
for (int i = 0; i <= puzzle.getSize(); i++) {
canvas.drawLine(i * cellSize, 0, i * cellSize, puzzle.getSize() * cellSize, paint);
canvas.drawLine(0, i * cellSize, puzzle.getSize() * cellSize, i * cellSize, paint);
}
// Affiche les points de couleur
paint.setStyle(Paint.Style.FILL);
for (int row = 0; row < puzzle.getSize(); row++) {
for (int col = 0; col < puzzle.getSize(); col++) {
int colorId = board[row][col];
if (colorId > 0) { // permet de ne pas dessiner des cercle quand je fais un trait
paint.setColor(getColorForId(colorId,isAchromate));
float cx = col * cellSize + cellSize / 2f;
float cy = row * cellSize + cellSize / 2f;
canvas.drawCircle(cx, cy, cellSize / 4f, paint);
}
}
}
// Affiche les chemins tracés
paint.setStrokeWidth(cellSize / 4f);
paint.setStyle(Paint.Style.STROKE);
for (Map.Entry<Integer, List<int[]>> entry : paths.entrySet()) {
paint.setColor(getColorForId(Math.abs(entry.getKey()),isAchromate));
List<int[]> path = entry.getValue();
for (int i = 0; i < path.size() - 1; i++) {
int[] p1 = path.get(i);
int[] p2 = path.get(i + 1);
canvas.drawLine(
p1[1] * cellSize + cellSize / 2f, p1[0] * cellSize + cellSize / 2f,
p2[1] * cellSize + cellSize / 2f, p2[0] * cellSize + cellSize / 2f,
paint
);
}
}
//dessine quand c'est victoir
if (win) {
String message = "🎉 Bravo !";
paint.setTextSize(80);
paint.setTextAlign(Paint.Align.CENTER);
// Mesure le texte
Paint.FontMetrics fm = paint.getFontMetrics();
float textWidth = paint.measureText(message);
float textHeight = fm.bottom - fm.top;
float centerX = getWidth() / 2f;
float centerY = getHeight() / 2f;
// 🎨 Dessine un fond blanc arrondi
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
float padding = 30;
RectF background = new RectF(
centerX - textWidth / 2 - padding,
centerY + fm.top - padding / 2,
centerX + textWidth / 2 + padding,
centerY + fm.bottom + padding / 2
);
canvas.drawRoundRect(background, 30, 30, paint);
// 🖤 Ombre
paint.setColor(Color.BLACK);
canvas.drawText(message, centerX + 3, centerY + 3, paint);
// 🔴 Texte principal
paint.setColor(Color.RED);
canvas.drawText(message, centerX, centerY, paint);
}
}
//test
private void clearPath(int color) {
// Supprime visuellement le chemin
for (int r = 0; r < board.length; r++) {
for (int c = 0; c < board[r].length; c++) {
if (board[r][c] == -color) {
board[r][c] = 0;
}
}
}
// Supprime le chemin logique
paths.remove(color);
completedColors.remove(color);
invalidate(); // Redessine
}
//test
//verrifie condition victoir
private void checkWin() {
// 1. Vérifie si le tableau est complètement rempli
for (int r = 0; r < board.length; r++) {
for (int c = 0; c < board[r].length; c++) {
if (board[r][c] == 0) {
return; // Case vide : pas encore gagné
}
}
}
// 2. Vérifie si toutes les paires sont terminées
if (completedColors.size() == puzzle.getPairs().size()) {
win = true;
invalidate(); // Redessine pour afficher le message
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int col = (int) (event.getX() / cellSize);
int row = (int) (event.getY() / cellSize);
if (col < 0 || row < 0 || col >= puzzle.getSize() || row >= puzzle.getSize())
return false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
int colorAtCell = board[row][col];
if (colorAtCell > 0) {
clearPath(colorAtCell); // 👈 même fonction de nettoyage
selectedColor = colorAtCell;
paths.put(selectedColor, new ArrayList<>());
paths.get(selectedColor).add(new int[]{row, col});
if (completedColors.contains(colorAtCell)) {// regarde si le chemin qu'on veut tracer est fini
// 🔒 Ne pas toucher à un chemin terminé
return true;
}
selectedColor = colorAtCell;
if (!paths.containsKey(selectedColor)) {
paths.put(selectedColor, new ArrayList<>());
}
// Si on relâche et reprend depuis la dernière case
List<int[]> path = paths.get(selectedColor);
if (path.isEmpty() || (path.get(path.size() - 1)[0] != row || path.get(path.size() - 1)[1] != col)) {
path.add(new int[]{row, col});
}
}
break;
case MotionEvent.ACTION_MOVE:
if (selectedColor != 0) {
List<int[]> path = paths.get(selectedColor);
if (path == null || path.isEmpty()) break;
int[] last = path.get(path.size() - 1);
int dr = Math.abs(last[0] - row);
int dc = Math.abs(last[1] - col);
if ((dr == 1 && dc == 0) || (dr == 0 && dc == 1)) {
int current = board[row][col];
// 1⃣ Avancer sur une case vide
if (current == 0) {
board[row][col] = -selectedColor;
path.add(new int[]{row, col});
invalidate();
}
// 2⃣ Atteindre lautre point de la paire
else if (current == selectedColor) {
// ⚠️ Ne pas revenir au point de départ
int[] start = path.get(0);
if (row == start[0] && col == start[1]) {
break;
}
path.add(new int[]{row, col});
board[row][col] = selectedColor;
completedColors.add(selectedColor);
selectedColor = 0;
checkWin();
invalidate();
}
// 3⃣ Reculer sur une case du chemin
else if (current == -selectedColor && path.size() >= 2) {
int[] beforeLast = path.get(path.size() - 2);
if (beforeLast[0] == row && beforeLast[1] == col) {
// 🔙 On recule dune case
int[] removed = path.remove(path.size() - 1);
board[removed[0]][removed[1]] = 0;
invalidate();
}
}
}
}
break;
case MotionEvent.ACTION_UP:
selectedColor = 0; // Permet de reprendre à la prochaine touche
break;
}
invalidate();
return true;
}
// Attribue une couleur vive et contrastée à chaque identifiant de point
private int getColorForId(int id,boolean achromate) {
if(achromate) {
if (!colorMap.containsKey(id)) {
int index = colorMap.size() % DISTINCT_GRIS.length;
colorMap.put(id, DISTINCT_GRIS[index]);
}
return colorMap.get(id);
}
else {
if (!colorMap.containsKey(id)) {
int index = colorMap.size() % DISTINCT_COLORS.length;
colorMap.put(id, DISTINCT_COLORS[index]);
}
return colorMap.get(id);
}
}
}