Shape.java

1
package com.takenoko.shape;
2
3
import com.takenoko.layers.tile.Tile;
4
import com.takenoko.vector.PositionVector;
5
import java.util.*;
6
import java.util.stream.IntStream;
7
import org.apache.commons.lang3.tuple.Pair;
8
9
/** Class representing a Shape. */
10
public class Shape {
11
    public static final IllegalArgumentException THE_SHAPE_CANNOT_BE_EMPTY_EXCEPTION =
12
            new IllegalArgumentException("The shape cannot be empty");
13
    private final Map<PositionVector, Tile> elements;
14
    private final PositionVector defaultRotationOrigin;
15
16
    /**
17
     * Constructor for the Shape class.
18
     *
19
     * @param shape the pattern of the shape
20
     * @param defaultRotationOrigin the default rotation origin of the shape
21
     */
22
    public Shape(Map<PositionVector, Tile> shape, PositionVector defaultRotationOrigin) {
23 1 1. <init> : negated conditional → KILLED
        if (shape.isEmpty()) {
24
            throw THE_SHAPE_CANNOT_BE_EMPTY_EXCEPTION;
25
        }
26
        this.elements = shape;
27
        this.defaultRotationOrigin = defaultRotationOrigin;
28
    }
29
30
    /**
31
     * Constructor for the Shape class. The rotation origin is the element the closest to the origin
32
     * of the coordinate system.
33
     *
34
     * @param shape the pattern of the shape
35
     */
36
    public Shape(Map<PositionVector, Tile> shape) {
37
        this(shape, findOrigin(shape));
38
    }
39
40
    /**
41
     * Find the rotation origin of the shape.
42
     *
43
     * @param shape the pattern of the shape
44
     * @return the rotation origin of the shape
45
     */
46
    private static PositionVector findOrigin(Map<PositionVector, Tile> shape) {
47 1 1. findOrigin : replaced return value with null for com/takenoko/shape/Shape::findOrigin → KILLED
        return shape.keySet().stream()
48
                .min(
49
                        (v1, v2) -> {
50
                            double v1Distance = v1.distance(new PositionVector(0, 0, 0));
51
                            double v2Distance = v2.distance(new PositionVector(0, 0, 0));
52 1 1. lambda$findOrigin$0 : replaced int return with 0 for com/takenoko/shape/Shape::lambda$findOrigin$0 → TIMED_OUT
                            return Double.compare(v1Distance, v2Distance);
53
                        })
54
                .orElse(new PositionVector(0, 0, 0));
55
    }
56
57
    /**
58
     * Constructor of the Shape class. To facilitate the creation of the shape, the shape is defined
59
     * by a list of vectors. The rotation origin is the element the closest to the origin of the
60
     * coordinate
61
     *
62
     * @param vectors the vectors of the shape
63
     */
64
    public Shape(PositionVector... vectors) {
65 1 1. <init> : negated conditional → NO_COVERAGE
        if (vectors.length == 0) {
66
            throw THE_SHAPE_CANNOT_BE_EMPTY_EXCEPTION;
67
        }
68
        this.elements = new HashMap<>();
69
        this.defaultRotationOrigin = findOrigin(this.elements);
70
        for (PositionVector vector : vectors) {
71
            this.elements.put(vector, new Tile());
72
        }
73
    }
74
75
    public Shape(Shape shape) {
76
        this.elements = new HashMap<>(shape.elements);
77
        this.defaultRotationOrigin = shape.defaultRotationOrigin;
78
    }
79
80
    /**
81
     * Constructor of the Shape class. To facilitate the creation of the shape, the shape is defined
82
     * by a list of Pair of vectors and tiles. The rotation origin is the element the closest to the
83
     * origin of the coordinate system.
84
     */
85
    @SafeVarargs
86
    public Shape(Pair<PositionVector, Tile>... vectors) {
87 1 1. <init> : negated conditional → KILLED
        if (vectors.length == 0) {
88
            throw THE_SHAPE_CANNOT_BE_EMPTY_EXCEPTION;
89
        }
90
        this.elements = new HashMap<>();
91
        this.defaultRotationOrigin = findOrigin(this.elements);
92
        for (Pair<PositionVector, Tile> vector : vectors) {
93
            this.elements.put(vector.getLeft(), vector.getRight());
94
        }
95
    }
96
97
    public Map<PositionVector, Tile> getElements() {
98 1 1. getElements : replaced return value with Collections.emptyMap for com/takenoko/shape/Shape::getElements → KILLED
        return this.elements;
99
    }
100
101
    /**
102
     * Returns a new shape with the same pattern but rotated 60 degrees around the given rotation
103
     * rotationOrigin. see <a
104
     * href="https://www.redblobgames.com/grids/hexagons/#rotation">https://www.redblobgames.com/grids/hexagons/#rotation</a>
105
     *
106
     * @param rotationOrigin pivot point of the rotation
107
     * @return the rotated shape
108
     */
109
    public Shape rotate60(PositionVector rotationOrigin) {
110 1 1. rotate60 : replaced return value with null for com/takenoko/shape/Shape::rotate60 → KILLED
        return new Shape(
111
                this.elements.entrySet().stream()
112
                        .map(
113
                                v ->
114 1 1. lambda$rotate60$1 : replaced return value with null for com/takenoko/shape/Shape::lambda$rotate60$1 → KILLED
                                        Pair.of(
115
                                                v.getKey()
116
                                                        .sub(rotationOrigin)
117
                                                        .rotate60()
118
                                                        .add(rotationOrigin)
119
                                                        .toPositionVector(),
120
                                                v.getValue()))
121
                        .collect(
122
                                HashMap::new,
123
                                (m, v) -> m.put(v.getLeft(), v.getRight()),
124
                                HashMap::putAll),
125
                rotationOrigin);
126
    }
127
128
    /**
129
     * Returns a new shape with the same pattern but rotated 60 degrees around the rotation origin
130
     * of the shape.
131
     *
132
     * @return the shape rotated 60 degrees around the first element of the pattern
133
     */
134
    public Shape rotate60() {
135 1 1. rotate60 : replaced return value with null for com/takenoko/shape/Shape::rotate60 → KILLED
        return this.rotate60(this.defaultRotationOrigin);
136
    }
137
138
    public Set<Shape> getRotatedShapes() {
139
        // return a list of shapes rotated in all directions
140 1 1. getRotatedShapes : replaced return value with Collections.emptySet for com/takenoko/shape/Shape::getRotatedShapes → KILLED
        return IntStream.range(0, 6) // From 0 to 5
141
                .mapToObj(this::getRotatedShape)
142
                .collect(HashSet::new, HashSet::add, HashSet::addAll);
143
    }
144
145
    public Shape getRotatedShape(int i) {
146
        Shape rotatedShape = this.copy();
147 2 1. getRotatedShape : negated conditional → TIMED_OUT
2. getRotatedShape : changed conditional boundary → KILLED
        for (int j = 0; j < i; j++) {
148
            rotatedShape = rotatedShape.rotate60();
149
        }
150 1 1. getRotatedShape : replaced return value with null for com/takenoko/shape/Shape::getRotatedShape → KILLED
        return rotatedShape;
151
    }
152
153
    /**
154
     * Returns a new shape with the same pattern but translated by the given vector.
155
     *
156
     * @param vector the vector to translate the shape by
157
     * @return a new shape with the same pattern but translated by the given vector
158
     */
159
    public Shape translate(PositionVector vector) {
160 1 1. translate : replaced return value with null for com/takenoko/shape/Shape::translate → KILLED
        return new Shape(
161
                this.elements.entrySet().stream()
162 1 1. lambda$translate$3 : replaced return value with null for com/takenoko/shape/Shape::lambda$translate$3 → KILLED
                        .map(v -> Pair.of(v.getKey().add(vector).toPositionVector(), v.getValue()))
163
                        .collect(
164
                                HashMap::new,
165
                                (m, v) -> m.put(v.getLeft(), v.getRight()),
166
                                HashMap::putAll),
167
                this.defaultRotationOrigin.add(vector).toPositionVector());
168
    }
169
170
    @Override
171
    public boolean equals(Object o) {
172 2 1. equals : negated conditional → KILLED
2. equals : replaced boolean return with false for com/takenoko/shape/Shape::equals → KILLED
        if (this == o) return true;
173 3 1. equals : negated conditional → KILLED
2. equals : negated conditional → KILLED
3. equals : replaced boolean return with true for com/takenoko/shape/Shape::equals → KILLED
        if (o == null || getClass() != o.getClass()) return false;
174
        Shape shape = (Shape) o;
175 2 1. equals : replaced boolean return with false for com/takenoko/shape/Shape::equals → KILLED
2. equals : replaced boolean return with true for com/takenoko/shape/Shape::equals → KILLED
        return getElements().equals(shape.getElements());
176
    }
177
178
    @Override
179
    public int hashCode() {
180 1 1. hashCode : replaced int return with 0 for com/takenoko/shape/Shape::hashCode → KILLED
        return Objects.hash(getElements());
181
    }
182
183
    public Shape copy() {
184 1 1. copy : replaced return value with null for com/takenoko/shape/Shape::copy → KILLED
        return new Shape(this.elements, this.defaultRotationOrigin);
185
    }
186
187
    @Override
188
    public String toString() {
189 1 1. toString : replaced return value with "" for com/takenoko/shape/Shape::toString → SURVIVED
        return "Shape{" + "pattern=" + elements + ", rotationOrigin=" + defaultRotationOrigin + '}';
190
    }
191
192
    public Shape getMissingShape(Shape other) {
193 1 1. getMissingShape : replaced return value with null for com/takenoko/shape/Shape::getMissingShape → KILLED
        return new Shape(
194
                this.elements.entrySet().stream()
195 2 1. lambda$getMissingShape$5 : negated conditional → KILLED
2. lambda$getMissingShape$5 : replaced boolean return with true for com/takenoko/shape/Shape::lambda$getMissingShape$5 → KILLED
                        .filter(e -> !other.elements.containsKey(e.getKey()))
196
                        .collect(
197
                                HashMap::new,
198
                                (m, v) -> m.put(v.getKey(), v.getValue()),
199
                                HashMap::putAll),
200
                this.defaultRotationOrigin);
201
    }
202
}

Mutations

23

1.1
Location : <init>
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestGetRotatedShape]/[method:getRotatedShape_shouldReturnNewShapeWithSameSize()]
negated conditional → KILLED

47

1.1
Location : findOrigin
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestTranslate]/[method:translate_shouldReturnNewShapeWithSameSize()]
replaced return value with null for com/takenoko/shape/Shape::findOrigin → KILLED

52

1.1
Location : lambda$findOrigin$0
Killed by : none
replaced int return with 0 for com/takenoko/shape/Shape::lambda$findOrigin$0 → TIMED_OUT

65

1.1
Location : <init>
Killed by : none
negated conditional → NO_COVERAGE

87

1.1
Location : <init>
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestEquals]/[method:equals_shouldReturnFalseWhenShapeIsNull()]
negated conditional → KILLED

98

1.1
Location : getElements
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestHashCode]/[method:hashCode_shouldReturnDifferentHashCodeWhenShapeHasDifferentElementsPosition()]
replaced return value with Collections.emptyMap for com/takenoko/shape/Shape::getElements → KILLED

110

1.1
Location : rotate60
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestGetRotatedShape]/[method:getRotatedShape_shouldReturnSameShapeWhenRotationIsMultipleOf360()]
replaced return value with null for com/takenoko/shape/Shape::rotate60 → KILLED

114

1.1
Location : lambda$rotate60$1
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestGetRotatedShape]/[method:getRotatedShape_shouldReturnNewShapeWithTilesRotated60Degrees()]
replaced return value with null for com/takenoko/shape/Shape::lambda$rotate60$1 → KILLED

135

1.1
Location : rotate60
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestGetRotatedShape]/[method:getRotatedShape_shouldReturnSameShapeWhenRotationIsMultipleOf360()]
replaced return value with null for com/takenoko/shape/Shape::rotate60 → KILLED

140

1.1
Location : getRotatedShapes
Killed by : com.takenoko.shape.PatternTest.[engine:junit-jupiter]/[class:com.takenoko.shape.PatternTest]/[nested-class:TestHashCode]/[method:hashCode_shouldReturnDifferentHashCodeWhenPatternsAreNotEqual()]
replaced return value with Collections.emptySet for com/takenoko/shape/Shape::getRotatedShapes → KILLED

147

1.1
Location : getRotatedShape
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestGetRotatedShape]/[method:getRotatedShape_shouldReturnNewShapeWithTilesRotated60Degrees()]
changed conditional boundary → KILLED

2.2
Location : getRotatedShape
Killed by : none
negated conditional → TIMED_OUT

150

1.1
Location : getRotatedShape
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestGetRotatedShape]/[method:getRotatedShape_shouldReturnNewShapeWithSameSize()]
replaced return value with null for com/takenoko/shape/Shape::getRotatedShape → KILLED

160

1.1
Location : translate
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestTranslate]/[method:translate_shouldReturnNewShapeWithSameSize()]
replaced return value with null for com/takenoko/shape/Shape::translate → KILLED

162

1.1
Location : lambda$translate$3
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestTranslate]/[method:translate_shouldReturnNewShapeWithSameSize()]
replaced return value with null for com/takenoko/shape/Shape::lambda$translate$3 → KILLED

172

1.1
Location : equals
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestEquals]/[method:equals_shouldReturnFalseWhenShapeIsNull()]
negated conditional → KILLED

2.2
Location : equals
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestEquals]/[method:equals_shouldReturnTrueWhenShapeIsItself()]
replaced boolean return with false for com/takenoko/shape/Shape::equals → KILLED

173

1.1
Location : equals
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestEquals]/[method:equals_shouldReturnFalseWhenShapeIsNull()]
negated conditional → KILLED

2.2
Location : equals
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestEquals]/[method:equals_shouldReturnFalseWhenShapeIsOfAnotherClass()]
negated conditional → KILLED

3.3
Location : equals
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestEquals]/[method:equals_shouldReturnFalseWhenShapeIsNull()]
replaced boolean return with true for com/takenoko/shape/Shape::equals → KILLED

175

1.1
Location : equals
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestEquals]/[method:equals_shouldReturnTrueWhenShapeHasSameElements()]
replaced boolean return with false for com/takenoko/shape/Shape::equals → KILLED

2.2
Location : equals
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestEquals]/[method:equals_shouldReturnFalseWhenShapeHasDifferentElementsColor()]
replaced boolean return with true for com/takenoko/shape/Shape::equals → KILLED

180

1.1
Location : hashCode
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestHashCode]/[method:hashCode_shouldReturnDifferentHashCodeWhenShapeHasDifferentElementsPosition()]
replaced int return with 0 for com/takenoko/shape/Shape::hashCode → KILLED

184

1.1
Location : copy
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestGetRotatedShape]/[method:getRotatedShape_shouldReturnNewShapeWithSameSize()]
replaced return value with null for com/takenoko/shape/Shape::copy → KILLED

189

1.1
Location : toString
Killed by : none
replaced return value with "" for com/takenoko/shape/Shape::toString → SURVIVED

193

1.1
Location : getMissingShape
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestGetMissingShape]/[method:getMissingShape_shouldReturnShapeCorrespondingToMissingPartOfShape()]
replaced return value with null for com/takenoko/shape/Shape::getMissingShape → KILLED

195

1.1
Location : lambda$getMissingShape$5
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestGetMissingShape]/[method:getMissingShape_shouldReturnShapeCorrespondingToMissingPartOfShape()]
negated conditional → KILLED

2.2
Location : lambda$getMissingShape$5
Killed by : com.takenoko.shape.ShapeTest.[engine:junit-jupiter]/[class:com.takenoko.shape.ShapeTest]/[nested-class:TestGetMissingShape]/[method:getMissingShape_shouldReturnShapeCorrespondingToMissingPartOfShape()]
replaced boolean return with true for com/takenoko/shape/Shape::lambda$getMissingShape$5 → KILLED

Active mutators

Tests examined


Report generated by PIT 1.8.0