Fix graph layout and edge labels
The graph now occupies the full screen width, and approximately one-third of the edges are left without constraints to improve spacing and reduce visual clutter.
This commit is contained in:
parent
9b07ffe9d9
commit
48c3f7a97a
1 changed files with 98 additions and 87 deletions
|
|
@ -76,15 +76,25 @@ const DogsBunnyPuzzle: React.FC<DogsBunnyPuzzleProps> = ({
|
|||
const conditionPrefixes = ['SOMEBODY AT', 'NOBODY AT'];
|
||||
|
||||
// Connect all locations with conditional edges
|
||||
let edgeCount = 0;
|
||||
const totalPossibleEdges = locations.length * (locations.length - 1);
|
||||
const freeEdgePercentage = 0.33; // About 1/3 of edges will be free
|
||||
|
||||
for (let i = 0; i < locations.length; i++) {
|
||||
for (let j = i + 1; j < locations.length; j++) {
|
||||
const numConditions = diff === 'easy' ? 1 : diff === 'medium' ? 2 : 3;
|
||||
const conditions: string[] = [];
|
||||
// Decide if this edge should be free (no conditions)
|
||||
const shouldBeFree = edgeCount < totalPossibleEdges * freeEdgePercentage && rng() < 0.5;
|
||||
|
||||
for (let k = 0; k < numConditions; k++) {
|
||||
const conditionType = locationTypes[Math.floor(rng() * locationTypes.length)];
|
||||
const prefix = conditionPrefixes[Math.floor(rng() * conditionPrefixes.length)];
|
||||
conditions.push(`${prefix} ${conditionType.toUpperCase()}`);
|
||||
let conditions: string[] = [];
|
||||
|
||||
if (!shouldBeFree) {
|
||||
const numConditions = diff === 'easy' ? 1 : diff === 'medium' ? 2 : 3;
|
||||
|
||||
for (let k = 0; k < numConditions; k++) {
|
||||
const conditionType = locationTypes[Math.floor(rng() * locationTypes.length)];
|
||||
const prefix = conditionPrefixes[Math.floor(rng() * conditionPrefixes.length)];
|
||||
conditions.push(`${prefix} ${conditionType.toUpperCase()}`);
|
||||
}
|
||||
}
|
||||
|
||||
edges.push({
|
||||
|
|
@ -98,6 +108,8 @@ const DogsBunnyPuzzle: React.FC<DogsBunnyPuzzleProps> = ({
|
|||
to: locations[i].id,
|
||||
conditions: conditions
|
||||
});
|
||||
|
||||
edgeCount += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -339,14 +351,12 @@ const DogsBunnyPuzzle: React.FC<DogsBunnyPuzzleProps> = ({
|
|||
</CardHeader>
|
||||
|
||||
<CardContent>
|
||||
<div className="flex flex-col lg:flex-row gap-6">
|
||||
{/* Puzzle Area */}
|
||||
<div className="flex-1">
|
||||
<div
|
||||
id="puzzle-area"
|
||||
className="relative bg-muted/10 rounded-lg p-4 min-h-[600px] w-full"
|
||||
style={{ height: '600px' }}
|
||||
>
|
||||
{/* Puzzle Area - Full Width */}
|
||||
<div
|
||||
id="puzzle-area"
|
||||
className="relative bg-muted/10 rounded-lg p-6 min-h-[700px] w-full mb-6"
|
||||
style={{ height: '700px' }}
|
||||
>
|
||||
{/* Edges */}
|
||||
<svg className="absolute inset-0 w-full h-full pointer-events-none">
|
||||
{puzzleState.edges.map((edge, index) => {
|
||||
|
|
@ -369,53 +379,55 @@ const DogsBunnyPuzzle: React.FC<DogsBunnyPuzzleProps> = ({
|
|||
strokeWidth={isValid ? "3" : "1"}
|
||||
strokeDasharray={isValid ? "none" : "5,5"}
|
||||
/>
|
||||
{/* Condition labels - simplified to show first condition only */}
|
||||
<g>
|
||||
{edge.conditions.length === 1 ? (
|
||||
// Single condition
|
||||
<circle
|
||||
cx={(fromLoc.x + toLoc.x) / 2 + 20}
|
||||
cy={(fromLoc.y + toLoc.y) / 2 + 20}
|
||||
r="12"
|
||||
fill="hsl(var(--background))"
|
||||
stroke={edge.conditions[0].includes('NOBODY') ? "#ef4444" : "hsl(var(--border))"}
|
||||
strokeWidth="2"
|
||||
/>
|
||||
) : (
|
||||
// Multiple conditions - show as rounded rectangle with dots
|
||||
<rect
|
||||
x={(fromLoc.x + toLoc.x) / 2 + 20 - 15}
|
||||
y={(fromLoc.y + toLoc.y) / 2 + 8}
|
||||
width="30"
|
||||
height="24"
|
||||
rx="12"
|
||||
fill="hsl(var(--background))"
|
||||
stroke={edge.conditions.some(c => c.includes('NOBODY')) ? "#ef4444" : "hsl(var(--border))"}
|
||||
strokeWidth="2"
|
||||
/>
|
||||
)}
|
||||
<text
|
||||
x={(fromLoc.x + toLoc.x) / 2 + 20}
|
||||
y={(fromLoc.y + toLoc.y) / 2 + 25}
|
||||
fontSize="14"
|
||||
textAnchor="middle"
|
||||
className="pointer-events-none"
|
||||
>
|
||||
{getConditionIcon(edge.conditions[0])}
|
||||
</text>
|
||||
{edge.conditions.length > 1 && (
|
||||
{/* Condition labels - only show if edge has conditions */}
|
||||
{edge.conditions.length > 0 && (
|
||||
<g>
|
||||
{edge.conditions.length === 1 ? (
|
||||
// Single condition
|
||||
<circle
|
||||
cx={(fromLoc.x + toLoc.x) / 2 + 30}
|
||||
cy={(fromLoc.y + toLoc.y) / 2 + 30}
|
||||
r="14"
|
||||
fill="hsl(var(--background))"
|
||||
stroke={edge.conditions[0].includes('NOBODY') ? "#ef4444" : "hsl(var(--border))"}
|
||||
strokeWidth="2"
|
||||
/>
|
||||
) : (
|
||||
// Multiple conditions - show as rounded rectangle
|
||||
<rect
|
||||
x={(fromLoc.x + toLoc.x) / 2 + 30 - 16}
|
||||
y={(fromLoc.y + toLoc.y) / 2 + 16}
|
||||
width="32"
|
||||
height="28"
|
||||
rx="14"
|
||||
fill="hsl(var(--background))"
|
||||
stroke={edge.conditions.some(c => c.includes('NOBODY')) ? "#ef4444" : "hsl(var(--border))"}
|
||||
strokeWidth="2"
|
||||
/>
|
||||
)}
|
||||
<text
|
||||
x={(fromLoc.x + toLoc.x) / 2 + 32}
|
||||
y={(fromLoc.y + toLoc.y) / 2 + 16}
|
||||
fontSize="8"
|
||||
x={(fromLoc.x + toLoc.x) / 2 + 30}
|
||||
y={(fromLoc.y + toLoc.y) / 2 + 36}
|
||||
fontSize="16"
|
||||
textAnchor="middle"
|
||||
className="pointer-events-none fill-muted-foreground"
|
||||
className="pointer-events-none"
|
||||
>
|
||||
+{edge.conditions.length - 1}
|
||||
{getConditionIcon(edge.conditions[0])}
|
||||
</text>
|
||||
)}
|
||||
<title>{edge.conditions.map(formatCondition).join(' AND ')}</title>
|
||||
</g>
|
||||
{edge.conditions.length > 1 && (
|
||||
<text
|
||||
x={(fromLoc.x + toLoc.x) / 2 + 42}
|
||||
y={(fromLoc.y + toLoc.y) / 2 + 22}
|
||||
fontSize="10"
|
||||
textAnchor="middle"
|
||||
className="pointer-events-none fill-muted-foreground"
|
||||
>
|
||||
+{edge.conditions.length - 1}
|
||||
</text>
|
||||
)}
|
||||
<title>{edge.conditions.map(formatCondition).join(' AND ')}</title>
|
||||
</g>
|
||||
)}
|
||||
</g>
|
||||
);
|
||||
})}
|
||||
|
|
@ -429,7 +441,7 @@ const DogsBunnyPuzzle: React.FC<DogsBunnyPuzzleProps> = ({
|
|||
return (
|
||||
<div
|
||||
key={location.id}
|
||||
className={`absolute w-10 h-10 rounded-full border-2 flex items-center justify-center cursor-pointer transition-all ${
|
||||
className={`absolute w-12 h-12 rounded-full border-2 flex items-center justify-center cursor-pointer transition-all ${
|
||||
isValidMove
|
||||
? 'border-primary bg-primary/20 scale-110'
|
||||
: 'border-border bg-background hover:bg-muted'
|
||||
|
|
@ -440,21 +452,21 @@ const DogsBunnyPuzzle: React.FC<DogsBunnyPuzzleProps> = ({
|
|||
}}
|
||||
onClick={() => isValidMove && handleMove(location.id)}
|
||||
>
|
||||
<span className="text-lg">
|
||||
<span className="text-xl">
|
||||
{getLocationIcon(location.type)}
|
||||
</span>
|
||||
{/* Characters at this location */}
|
||||
{charactersHere.map((char, index) => (
|
||||
<div
|
||||
key={char.id}
|
||||
className={`absolute w-6 h-6 rounded-full border flex items-center justify-center cursor-pointer transition-all ${
|
||||
className={`absolute w-7 h-7 rounded-full border flex items-center justify-center cursor-pointer transition-all ${
|
||||
selectedCharacter === char.id
|
||||
? 'border-primary bg-primary text-primary-foreground scale-110'
|
||||
: 'border-border bg-background hover:bg-muted'
|
||||
}`}
|
||||
style={{
|
||||
right: -8 - (index * 12),
|
||||
top: -8 - (index * 12),
|
||||
right: -9 - (index * 14),
|
||||
top: -9 - (index * 14),
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
|
@ -469,11 +481,11 @@ const DogsBunnyPuzzle: React.FC<DogsBunnyPuzzleProps> = ({
|
|||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Instructions & Controls */}
|
||||
<div className="w-full lg:w-80 space-y-4">
|
||||
</div>
|
||||
|
||||
{/* Instructions & Controls - Below the graph */}
|
||||
<div className="flex flex-col lg:flex-row gap-6">
|
||||
<div className="flex-1 space-y-4">
|
||||
<div className="space-y-2">
|
||||
<h3 className="font-semibold">How to Play:</h3>
|
||||
<ul className="text-sm text-muted-foreground space-y-1">
|
||||
|
|
@ -498,26 +510,25 @@ const DogsBunnyPuzzle: React.FC<DogsBunnyPuzzleProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-2">
|
||||
<Button onClick={resetPuzzle} variant="outline" className="w-full">
|
||||
<RotateCcw className="h-4 w-4 mr-2" />
|
||||
Reset
|
||||
</Button>
|
||||
<Button onClick={generateNewPuzzle} variant="outline" className="w-full">
|
||||
<Shuffle className="h-4 w-4 mr-2" />
|
||||
New Puzzle
|
||||
</Button>
|
||||
<Button onClick={sharePuzzle} variant="outline" className="w-full">
|
||||
<Share2 className="h-4 w-4 mr-2" />
|
||||
Share
|
||||
</Button>
|
||||
<Button onClick={screenshotPuzzle} variant="outline" className="w-full">
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
Screenshot
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="lg:w-80 space-y-2">
|
||||
<Button onClick={resetPuzzle} variant="outline" className="w-full">
|
||||
<RotateCcw className="h-4 w-4 mr-2" />
|
||||
Reset
|
||||
</Button>
|
||||
<Button onClick={generateNewPuzzle} variant="outline" className="w-full">
|
||||
<Shuffle className="h-4 w-4 mr-2" />
|
||||
New Puzzle
|
||||
</Button>
|
||||
<Button onClick={sharePuzzle} variant="outline" className="w-full">
|
||||
<Share2 className="h-4 w-4 mr-2" />
|
||||
Share
|
||||
</Button>
|
||||
<Button onClick={screenshotPuzzle} variant="outline" className="w-full">
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
Screenshot
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue